일반적으로 사람들이 다음과 같이 클래스 리터럴을 사용하는 것을 보았습니다.
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")
첫 번째 예제에서 목록의 비 매개 변수 사용으로 인한 경고 없애,Class<List> cls = List.class
하지만, 차라리하지 않는 게 좋을.
어떤 제안?
답변
삭제 유형 으로 인해 수 없습니다 .
Java 제네릭은 Object 캐스트의 구문 설탕에 지나지 않습니다. 시연하려면 :
List<Integer> list1 = new ArrayList<Integer>();
List<String> list2 = (List<String>)list1;
list2.add("foo"); // perfectly legal
런타임에 일반 형식 정보가 유지되는 유일한 경우는 Field.getGenericType()
리플렉션을 통해 클래스 멤버를 심문하는 경우입니다.
이 모든 Object.getClass()
것이이 서명 이있는 이유입니다 .
public final native Class<?> getClass();
중요한 부분은 Class<?>
입니다.
Java Generics FAQ 에서 다른 방법으로 넣으려면 :
구체적인 매개 변수화 된 유형에 대한 클래스 리터럴이없는 이유는 무엇입니까?
매개 변수화 된 유형에는 정확한 런타임 유형 표현이 없기 때문입니다.
클래스 리터럴은
Class
주어진 유형을 나타내는 객체를 나타냅니다. 예를 들어 클래스 리터럴
String.class
은Class
유형을 나타내는 객체를 나타내며
객체에서 메서드 를 호출
할 때 반환String
되는Class
객체 와 동일
합니다. 클래스 리터럴은 런타임 유형 확인 및 반영에 사용될 수 있습니다.getClass
String
매개 변수화 된 유형은 유형 삭제라는 프로세스에서 컴파일 중에 바이트 코드로 변환 될 때 유형 인수를 잃습니다. 유형 삭제의 부작용으로 일반 유형의 모든 인스턴스화는 동일한 런타임 표현, 즉 해당 원시 유형의 인스턴스화를 공유합니다. 다시 말해, 매개 변수화 된 유형에는 자체 유형이 없습니다. 따라서,이 같은 클래스 리터럴을 형성 소용이없고
List<String>.class
,
List<Long>.class
그리고List<?>.class
그러한 때문에,Class
물체가 존재하지 않는다. 만 원시 타입은List
가Class
자사의 실행시의 형태를 나타내는 개체를. 이라고합니다
List.class
.
답변
매개 변수화 된 형식에 대한 클래스 리터럴은 없지만 이러한 형식을 올바르게 정의하는 Type 개체가 있습니다.
java.lang.reflect.ParameterizedType 참조-http: //java.sun.com/j2se/1.5.0/docs/api/java/lang/reflect/ParameterizedType.html
Google의 Gson 라이브러리는 단순히 매개 변수화 된 유형을 생성하고이를 사용하여 복잡한 매개 변수화 된 유형을 가진 json 객체를 일반적인 친숙한 방식으로 지정하는 데 사용되는 TypeToken 클래스를 정의합니다. 귀하의 예에서는 다음을 사용합니다.
Type typeOfListOfFoo = new TypeToken<List<Foo>>(){}.getType()
javadoc TypeToken 및 Gson 클래스에 대한 링크를 게시하려고했지만 새 사용자이므로 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();
이를 통해 컴파일러는 컴파일 타임에 코드를 확인할 수 있지만 런타임에는 여전히 첫 번째 예제처럼 보입니다.
답변
자바 제네릭 FAQ 따라서도 클리 터스 ‘ 응답 가지고있는 중심점이 없기처럼 소리 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<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);
}