Java: 범용 유형에서 클래스 리터럴을 가져오려면 어떻게 해야 합니까?
보통, 나는 사람들이 클래스 리터럴을 다음과 같이 사용하는 것을 봐왔다.
Class<Foo> cls = Foo.class;
그러나 그 유형이 일반적이면(예: 목록) 어떻게 해야 합니까?이 조작은 정상적으로 동작하지만 List는 파라미터화되어야 하므로 경고가 표시됩니다.
Class<List> cls = List.class
'우리'를 건 요?<?>
에러의 이 됩니다.
Class<List<?>> cls = List.class
다음과 같은 것이 동작할 것으로 생각했지만, 이것은 단순한 ol' 구문 오류입니다.
Class<List<Foo>> cls = List<Foo>.class
어떻게 하면 구할 수 있나요?Class<List<Foo>>
정적으로, 예를 들어 클래스 리터럴을 사용하는가?
나는 할 수 있다@SuppressWarnings("unchecked")
List를 사용합니다.Class<List> cls = List.class
하지만 안 하는 게 낫겠어요
좋은 의견이라도 있나?
활자 삭제로 인해 불가능합니다.
자바 제네릭스는 오브젝트 캐스트를 위한 통사 설탕에 지나지 않습니다.데모 방법:
List<Integer> list1 = new ArrayList<Integer>();
List<String> list2 = (List<String>)list1;
list2.add("foo"); // perfectly legal
일반 유형 정보가 런타임에 유지되는 유일한 예는 반사를 통해 클래스 멤버를 조회하는 경우입니다.
이 모든 것이, 다음의 시그니처를 가지는 이유입니다.
public final native Class<?> getClass();
은 '아까운' 부분이에요.Class<?>
.
바꿔 말하면 Java Generics FAQ에서 다음을 참조하십시오.
구체적인 매개 변수화된 유형에 대한 클래스 리터럴이 없는 이유는 무엇입니까?
매개 변수화된 유형에는 정확한 런타임 유형 표현이 없기 때문입니다.
은 ""를 .
Class
특정 유형을 나타내는 오브젝트.들어 리터럴은 " " " 입니다.String.class
는 을 .Class
을String
이 경우,Class
「」가되는 .getClass
String
및리플렉션에 할 수 .클래스 리터럴은 런타임 유형 검사 및 리플렉션에 사용할 수 있습니다.파라미터화된 타입은 타입 삭제라고 불리는 프로세스에서 컴파일 중에 바이트 코드로 변환되면 타입 인수가 손실됩니다.타입 삭제의 부작용으로 범용 타입의 인스턴스화는 모두 동일한 런타임 표현, 즉 대응하는 raw 타입의 인스턴스화를 공유합니다..결과적으로, 다음과 같은 계급 문학을 형성하는 것은 의미가 없다.
List<String>.class
,List<Long>.class
★★★★★★★★★★★★★★★★★」List<?>.class
「」는Class
오브젝트가 존재합니다.의 raw만List
가지고 있다Class
실행 시 유형을 나타내는 객체입니다. 고립불 it it it it it라고 .List.class
.
매개 변수화된 유형에 대한 클래스 리터럴은 없지만 이러한 유형을 올바르게 정의하는 유형 개체가 있습니다.
java.lang.reflect 참조.파라미터화입력 - http://java.sun.com/j2se/1.5.0/docs/api/java/lang/reflect/ParameterizedType.html
Google의 Gson 라이브러리는 TypeToken 클래스를 정의하여 단순히 매개 변수화된 유형을 생성할 수 있으며, 이를 사용하여 복잡한 매개 변수화된 유형을 가진 json 개체를 일반 친화적인 방식으로 지정합니다.이 예에서는 다음을 사용합니다.
Type typeOfListOfFoo = new TypeToken<List<Foo>>(){}.getType()
TypeToken 및 Gson 클래스에 대한 링크를 javadoc에 게시하려고 했는데 Stack Overflow는 새 사용자이기 때문에 여러 링크를 게시할 수 없습니다. Google 검색을 사용하면 쉽게 찾을 수 있습니다.
더블 캐스트로 관리할 수 있습니다.
@SuppressWarnings("unchecked") Class<List<Foo>> cls = (Class<List<Foo>>)(Object)List.class
cletus의 답변을 자세히 설명하기 위해 실행 시 일반 유형의 모든 레코드가 삭제됩니다.제네릭은 컴파일러에서만 처리되며 타입의 안전성을 높이기 위해 사용됩니다.컴파일러가 적절한 위치에 타이프캐스트를 삽입할 수 있도록 하는 단순한 줄임말입니다.예를 들어, 이전에는 다음을 수행해야 했습니다.
List x = new ArrayList();
x.add(new SomeClass());
Iterator i = x.iterator();
SomeClass z = (SomeClass) i.next();
된다
List<SomeClass> x = new ArrayList<SomeClass>();
x.add(new SomeClass());
Iterator<SomeClass> i = x.iterator();
SomeClass z = i.next();
이를 통해 컴파일러는 컴파일 시 코드를 체크할 수 있지만 런타임에는 여전히 첫 번째 예시와 같습니다.
도우미 방법을 사용하여 이 문제를 해결할 수 있습니다.@SuppressWarnings("unchecked")
반 전체에 걸쳐서
@SuppressWarnings("unchecked")
private static <T> Class<T> generify(Class<?> cls) {
return (Class<T>)cls;
}
그럼 글을 쓰세요.
Class<List<Foo>> cls = generify(List.class);
기타 사용 예는 다음과 같습니다.
Class<Map<String, Integer>> cls;
cls = generify(Map.class);
cls = TheClass.<Map<String, Integer>>generify(Map.class);
funWithTypeParam(generify(Map.class));
public void funWithTypeParam(Class<Map<String, Integer>> cls) {
}
다만, 실제로 도움이 되는 경우는 거의 없고, 메서드의 사용법도 컴파일러의 타입 체크에 어긋나기 때문에, 공개적인 장소에 실장하는 것은 추천하지 않습니다.
Java Generics FAQ와 그에 따른 cletus의 답변은 어떤 의미도 없는 것처럼 들립니다.Class<List<T>>
그러나 진짜 문제는 이것이 매우 위험하다는 것입니다.
@SuppressWarnings("unchecked")
Class<List<String>> stringListClass = (Class<List<String>>) (Class<?>) List.class;
List<Integer> intList = new ArrayList<>();
intList.add(1);
List<String> stringList = stringListClass.cast(intList);
// Surprise!
String firstElement = stringList.get(0);
그cast()
안전한 것처럼 보이지만 실제로는 전혀 안전하지 않습니다.
있을 수 없는 곳에 갈 수는 없지만List<?>.class
=Class<List<?>>
일반적인 타입에 근거해 타입을 결정하는 방법이 있는 경우, 이것은 매우 도움이 될 것입니다.Class
논쟁.
위해서getClass()
와일드카드 사용으로의 전환을 요구하는 JDK-6184881이 있습니다만, 이전 코드와 호환되지 않기 때문에 (곧) 이 변경이 실행될 것 같지는 않습니다(이 코멘트 참조).
우리 모두 알다시피 지워져.단, 클래스 계층에서 유형이 명시적으로 언급되는 상황에서는 알 수 있습니다.
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
public abstract class CaptureType<T> {
/**
* {@link java.lang.reflect.Type} object of the corresponding generic type. This method is useful to obtain every kind of information (including annotations) of the generic type.
*
* @return Type object. null if type could not be obtained (This happens in case of generic type whose information cant be obtained using Reflection). Please refer documentation of {@link com.types.CaptureType}
*/
public Type getTypeParam() {
Class<?> bottom = getClass();
Map<TypeVariable<?>, Type> reifyMap = new LinkedHashMap<>();
for (; ; ) {
Type genericSuper = bottom.getGenericSuperclass();
if (!(genericSuper instanceof Class)) {
ParameterizedType generic = (ParameterizedType) genericSuper;
Class<?> actualClaz = (Class<?>) generic.getRawType();
TypeVariable<? extends Class<?>>[] typeParameters = actualClaz.getTypeParameters();
Type[] reified = generic.getActualTypeArguments();
assert (typeParameters.length != 0);
for (int i = 0; i < typeParameters.length; i++) {
reifyMap.put(typeParameters[i], reified[i]);
}
}
if (bottom.getSuperclass().equals(CaptureType.class)) {
bottom = bottom.getSuperclass();
break;
}
bottom = bottom.getSuperclass();
}
TypeVariable<?> var = bottom.getTypeParameters()[0];
while (true) {
Type type = reifyMap.get(var);
if (type instanceof TypeVariable) {
var = (TypeVariable<?>) type;
} else {
return type;
}
}
}
/**
* Returns the raw type of the generic type.
* <p>For example in case of {@code CaptureType<String>}, it would return {@code Class<String>}</p>
* For more comprehensive examples, go through javadocs of {@link com.types.CaptureType}
*
* @return Class object
* @throws java.lang.RuntimeException If the type information cant be obtained. Refer documentation of {@link com.types.CaptureType}
* @see com.types.CaptureType
*/
public Class<T> getRawType() {
Type typeParam = getTypeParam();
if (typeParam != null)
return getClass(typeParam);
else throw new RuntimeException("Could not obtain type information");
}
/**
* Gets the {@link java.lang.Class} object of the argument type.
* <p>If the type is an {@link java.lang.reflect.ParameterizedType}, then it returns its {@link java.lang.reflect.ParameterizedType#getRawType()}</p>
*
* @param type The type
* @param <A> type of class object expected
* @return The Class<A> object of the type
* @throws java.lang.RuntimeException If the type is a {@link java.lang.reflect.TypeVariable}. In such cases, it is impossible to obtain the Class object
*/
public static <A> Class<A> getClass(Type type) {
if (type instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) type).getGenericComponentType();
Class<?> componentClass = getClass(componentType);
if (componentClass != null) {
return (Class<A>) Array.newInstance(componentClass, 0).getClass();
} else throw new UnsupportedOperationException("Unknown class: " + type.getClass());
} else if (type instanceof Class) {
Class claz = (Class) type;
return claz;
} else if (type instanceof ParameterizedType) {
return getClass(((ParameterizedType) type).getRawType());
} else if (type instanceof TypeVariable) {
throw new RuntimeException("The type signature is erased. The type class cant be known by using reflection");
} else throw new UnsupportedOperationException("Unknown class: " + type.getClass());
}
/**
* This method is the preferred method of usage in case of complex generic types.
* <p>It returns {@link com.types.TypeADT} object which contains nested information of the type parameters</p>
*
* @return TypeADT object
* @throws java.lang.RuntimeException If the type information cant be obtained. Refer documentation of {@link com.types.CaptureType}
*/
public TypeADT getParamADT() {
return recursiveADT(getTypeParam());
}
private TypeADT recursiveADT(Type type) {
if (type instanceof Class) {
return new TypeADT((Class<?>) type, null);
} else if (type instanceof ParameterizedType) {
ArrayList<TypeADT> generic = new ArrayList<>();
ParameterizedType type1 = (ParameterizedType) type;
return new TypeADT((Class<?>) type1.getRawType(),
Arrays.stream(type1.getActualTypeArguments()).map(x -> recursiveADT(x)).collect(Collectors.toList()));
} else throw new UnsupportedOperationException();
}
}
public class TypeADT {
private final Class<?> reify;
private final List<TypeADT> parametrized;
TypeADT(Class<?> reify, List<TypeADT> parametrized) {
this.reify = reify;
this.parametrized = parametrized;
}
public Class<?> getRawType() {
return reify;
}
public List<TypeADT> getParameters() {
return parametrized;
}
}
이제 다음과 같은 작업을 수행할 수 있습니다.
static void test1() {
CaptureType<String> t1 = new CaptureType<String>() {
};
equals(t1.getRawType(), String.class);
}
static void test2() {
CaptureType<List<String>> t1 = new CaptureType<List<String>>() {
};
equals(t1.getRawType(), List.class);
equals(t1.getParamADT().getParameters().get(0).getRawType(), String.class);
}
private static void test3() {
CaptureType<List<List<String>>> t1 = new CaptureType<List<List<String>>>() {
};
equals(t1.getParamADT().getRawType(), List.class);
equals(t1.getParamADT().getParameters().get(0).getRawType(), List.class);
}
static class Test4 extends CaptureType<List<String>> {
}
static void test4() {
Test4 test4 = new Test4();
equals(test4.getParamADT().getRawType(), List.class);
}
static class PreTest5<S> extends CaptureType<Integer> {
}
static class Test5 extends PreTest5<Integer> {
}
static void test5() {
Test5 test5 = new Test5();
equals(test5.getTypeParam(), Integer.class);
}
static class PreTest6<S> extends CaptureType<S> {
}
static class Test6 extends PreTest6<Integer> {
}
static void test6() {
Test6 test6 = new Test6();
equals(test6.getTypeParam(), Integer.class);
}
class X<T> extends CaptureType<T> {
}
class Y<A, B> extends X<B> {
}
class Z<Q> extends Y<Q, Map<Integer, List<List<List<Integer>>>>> {
}
void test7(){
Z<String> z = new Z<>();
TypeADT param = z.getParamADT();
equals(param.getRawType(), Map.class);
List<TypeADT> parameters = param.getParameters();
equals(parameters.get(0).getRawType(), Integer.class);
equals(parameters.get(1).getRawType(), List.class);
equals(parameters.get(1).getParameters().get(0).getRawType(), List.class);
equals(parameters.get(1).getParameters().get(0).getParameters().get(0).getRawType(), List.class);
equals(parameters.get(1).getParameters().get(0).getParameters().get(0).getParameters().get(0).getRawType(), Integer.class);
}
static void test8() throws IllegalAccessException, InstantiationException {
CaptureType<int[]> type = new CaptureType<int[]>() {
};
equals(type.getRawType(), int[].class);
}
static void test9(){
CaptureType<String[]> type = new CaptureType<String[]>() {
};
equals(type.getRawType(), String[].class);
}
static class SomeClass<T> extends CaptureType<T>{}
static void test10(){
SomeClass<String> claz = new SomeClass<>();
try{
claz.getRawType();
throw new RuntimeException("Shouldnt come here");
}catch (RuntimeException ex){
}
}
static void equals(Object a, Object b) {
if (!a.equals(b)) {
throw new RuntimeException("Test failed. " + a + " != " + b);
}
}
자세한 것은 이쪽.그러나 다음 항목에 대한 검색은 거의 불가능합니다.
class SomeClass<T> extends CaptureType<T>{}
SomeClass<String> claz = new SomeClass<>();
지워지는 곳이죠
Class Literals에는 범용 타입 정보가 없기 때문에 경고를 모두 없애는 것은 불가능하다고 생각합니다. 면에서는, 「」를 사용하고 .Class<Something>
는 범용 타입을 지정하지 않고 컬렉션을 사용하는 것과 같습니다.제가 할 수 있는 최선의 방법은
private <C extends A<C>> List<C> getList(Class<C> cls) {
List<C> res = new ArrayList<C>();
// "snip"... some stuff happening in here, using cls
return res;
}
public <C extends A<C>> List<A<C>> getList() {
return getList(A.class);
}
언급URL : https://stackoverflow.com/questions/2390662/java-how-do-i-get-a-class-literal-from-a-generic-type
'programing' 카테고리의 다른 글
기본 포인터와 스택 포인터란 정확히 무엇입니까?그들은 무엇을 가리키고 있나요? (0) | 2022.07.07 |
---|---|
vue-google-map set infow는 마커 위에 있습니다. (0) | 2022.07.07 |
Vuetifyjs 오류 알 수 없는 사용자 지정 요소: 구성 요소를 올바르게 등록했습니까? (0) | 2022.07.07 |
입력 스트림에서 효율적으로 Android 읽기 (0) | 2022.07.07 |
Vue 구성 요소에 Vuelidate $v가 정의되지 않았습니다. (0) | 2022.07.07 |