답변:
하나의 구조, 나는 한 번 넘어졌다
Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
불행히도 완전히 이해하지 못하는 주변에 반성 마법이있는 것 같습니다 ... 미안합니다.
Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
제약 조건이 무엇인지 잘 모르겠습니다.
java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
@DerMike의 답변을 세분화하여 설명하려고합니다.
첫째, 유형의 삭제는 그 JDK의 의미하지 않는다 을 제거해을 가 런타임에 유형 정보를 . 컴파일 타임 유형 검사 및 런타임 유형 호환성이 동일한 언어로 공존 할 수 있도록하는 방법입니다. 이 코드 블록에서 알 수 있듯이 JDK는 지워진 유형 정보를 유지합니다. 이는 확인 된 캐스트 및 항목과 관련이 없습니다.
둘째, 이것은 검사중인 콘크리트 유형에서 계층 구조의 정확히 한 레벨 위의 일반 클래스에 일반 유형 정보를 제공합니다. 그 으로부터 직접 상속받습니다. 이 클래스가 비 추상적이고 인스턴스화되었거나 구체적인 구현이 두 수준 아래로 내려간 경우에는 작동하지 않습니다 (조금의 약간의 지미로 인해 하나 이상의 클래스 또는 가장 낮은 클래스까지 미리 정해진 수의 레벨에 적용 할 수 있음) X 제네릭 형식 매개 변수 등).
어쨌든, 설명에. 다음은 쉽게 참조 할 수 있도록 코드를 다시 한 줄로 분리 한 것입니다.
1 # 클래스 genericParameter0OfThisClass = 2 # (클래스) 3 # ((매개 변수화 된 유형) 4 # getClass () 5 # .getGenericSuperclass ()) 6 # .getActualTypeArguments () [0];
이 코드를 포함하는 제네릭 형식의 추상 클래스 인 'us'를 보자. 이것을 대략 내부에서 읽는다.
... 그 정도입니다. 그래서 우리는 우리 자신의 구체적인 구현에서 타입 정보를 다시 우리 자신에게 푸시하고 그것을 사용하여 클래스 핸들에 액세스합니다. 우리는 getGenericSuperclass ()를 두 배로 늘리고 두 레벨로 가거나 getGenericSuperclass ()를 제거하고 구체적인 유형으로 가치를 얻을 수 있습니다 (캐비티 :이 시나리오를 테스트하지 않았지만 아직 나에게 오지 않았습니다).
구체적인 자녀가 홉을 임의의 수로 멀리 떨어 뜨리거나 구체적이지 않고 최종적이지 않은 경우 까다로워지고, (매우 깊은) 자녀 중 하나가 자신의 제네릭을 갖기를 기대하는 경우 특히 까다 롭습니다. 그러나 일반적으로 이러한 고려 사항을 중심으로 디자인 할 수 있으므로 대부분의 방법으로 얻을 수 있습니다.
이것이 누군가를 도왔기를 바랍니다! 이 게시물이 고대인 것 같습니다. 아마도이 설명을 잘라내어 다른 질문을 위해 보관할 것입니다.
실제로 나는 이것을 작동시켰다. 다음 스 니펫을 고려하십시오.
Method m;
Type[] genericParameterTypes = m.getGenericParameterTypes();
for (int i = 0; i < genericParameterTypes.length; i++) {
if( genericParameterTypes[i] instanceof ParameterizedType ) {
Type[] parameters = ((ParameterizedType)genericParameterTypes[i]).getActualTypeArguments();
//parameters[0] contains java.lang.String for method like "method(List<String> value)"
}
}
jdk 1.6을 사용하고 있습니다.
실제로 "익명 클래스"트릭 과 슈퍼 타입 토큰 의 아이디어를 적용하여 해결책이 있습니다 .
public final class Voodoo {
public static void chill(final List<?> aListWithSomeType) {
// Here I'd like to get the Class-Object 'SpiderMan'
System.out.println(aListWithSomeType.getClass().getGenericSuperclass());
System.out.println(((ParameterizedType) aListWithSomeType
.getClass()
.getGenericSuperclass()).getActualTypeArguments()[0]);
}
public static void main(String... args) {
chill(new ArrayList<SpiderMan>() {});
}
}
class SpiderMan {
}
의 창조의 트릭 거짓말 익명 클래스 , new ArrayList<SpiderMan>() {}
원래 (단순)의 장소에서 new ArrayList<SpiderMan>()
. 성가신 클래스 (가능한 경우)를 사용하면 컴파일러가 SpiderMan
type 매개 변수에 지정된 형식 인수에 대한 정보를 유지합니다 List<?>
. oil!
매개 변수화 된 인터페이스의 일반 매개 변수를 가져 오는 @DerMike의 답변 부록 ( 중복을 피하기 위해 Java-8 기본 메소드 내에서 #getGenericInterfaces () 메소드 사용 ) :
import java.lang.reflect.ParameterizedType;
public class ParametrizedStuff {
@SuppressWarnings("unchecked")
interface Awesomable<T> {
default Class<T> parameterizedType() {
return (Class<T>) ((ParameterizedType)
this.getClass().getGenericInterfaces()[0])
.getActualTypeArguments()[0];
}
}
static class Beer {};
static class EstrellaGalicia implements Awesomable<Beer> {};
public static void main(String[] args) {
System.out.println("Type is: " + new EstrellaGalicia().parameterizedType());
// --> Type is: ParameterizedStuff$Beer
}
Awesomeable<Beer>
. 이 경우 유형 정보가 유지됩니다. 당신 new Awesomable<Beer> ()
이 방법으로 전달 하면 wt 작동하지 않습니다.
Awesomable<Beer>
처럼 구체적인 서브 클래스의 명시 적으로 정의하지 않고 즉시 EstrellaGalicia
이 경우, 여전히이 파라미터 화 된 형태를 받고있다 : 나는 지금 그것을 실행 : System.out.println("Type is: " + new Awesomable<Beer>() {}.parameterizedType());
---> 유형은 다음과 같습니다 ParameterizedStuff $ 맥주
아니, 그건 불가능 해 호환성 호환성 문제로 인해 Java의 제네릭은 유형 삭제 (즉 , 런타임시)를 기반으로 List
합니다. 런타임에 형식 매개 변수에 대한 정보가 있지만 개체 정의가 아니라 클래스 정의에 있습니다 (예 : " 이 필드의 정의에서 어떤 일반 형식을 사용합니까? ").
@bertolami가 지적했듯이 변수 유형을 사용할 수 없으며 미래 가치 (typeOfList 변수의 내용)를 얻을 수 없습니다.
그럼에도 불구하고 다음과 같이 클래스를 매개 변수로 전달할 수 있습니다.
public final class voodoo {
public static void chill(List<T> aListWithTypeSpiderMan, Class<T> clazz) {
// Here I'd like to get the Class-Object 'SpiderMan'
Class typeOfTheList = clazz;
}
public static void main(String... args) {
chill(new List<SpiderMan>(), Spiderman.class );
}
}
그것은 클래스 변수를 ActivityInstrumentationTestCase2 의 생성자에 전달해야 할 때 Google 이하는 일 입니다.
불가능합니다.
클래스가 해당 규칙의 유일한 예외이며 심지어 약간의 해킹 인 경우 일반 유형의 필드를 얻을 수 있습니다.
이에 대한 예는 Java에서 일반 유형 알기를 참조하십시오 .
여기 에서 찾은이 예제와 같이 리플렉션이있는 일반 매개 변수의 유형을 얻을 수 있습니다 .
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
public class Home<E> {
@SuppressWarnings ("unchecked")
public Class<E> getTypeParameterClass(){
Type type = getClass().getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) type;
return (Class<E>) paramType.getActualTypeArguments()[0];
}
private static class StringHome extends Home<String>{}
private static class StringBuilderHome extends Home<StringBuilder>{}
private static class StringBufferHome extends Home<StringBuffer>{}
/**
* This prints "String", "StringBuilder" and "StringBuffer"
*/
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Object object0 = new StringHome().getTypeParameterClass().newInstance();
Object object1 = new StringBuilderHome().getTypeParameterClass().newInstance();
Object object2 = new StringBufferHome().getTypeParameterClass().newInstance();
System.out.println(object0.getClass().getSimpleName());
System.out.println(object1.getClass().getSimpleName());
System.out.println(object2.getClass().getSimpleName());
}
}
Java 제네릭 형식 지우기 때문에 질문에 대한 빠른 답변 은 없습니다.
더 긴 대답은 다음과 같이 목록을 만든 경우입니다.
new ArrayList<SpideMan>(){}
그런 다음이 경우 제네릭 형식은 위의 새 익명 클래스의 제네릭 수퍼 클래스에 유지됩니다.
목록 으로이 작업을 수행하는 것이 좋지는 않지만 리스너 구현입니다.
new Listener<Type>() { public void doSomething(Type t){...}}
그리고 일반적인 유형의 수퍼 클래스와 수퍼 인터페이스를 추정하면 JVM간에 변경되므로 일반적인 해결책은 일부 대답이 제안하는 것처럼 간단하지 않습니다.
Java의 제네릭은 컴파일 타임에만 고려되기 때문에 불가능합니다. 따라서 Java 제네릭은 일종의 전 처리기입니다. 그러나 목록 멤버의 실제 클래스를 얻을 수 있습니다.
accept 또는 return을 기대하는 메소드에 대해 이것을 코딩했습니다 Iterable<?...>
. 코드는 다음과 같습니다.
/**
* Assuming the given method returns or takes an Iterable<T>, this determines the type T.
* T may or may not extend WindupVertexFrame.
*/
private static Class typeOfIterable(Method method, boolean setter)
{
Type type;
if (setter) {
Type[] types = method.getGenericParameterTypes();
// The first parameter to the method expected to be Iterable<...> .
if (types.length == 0)
throw new IllegalArgumentException("Given method has 0 params: " + method);
type = types[0];
}
else {
type = method.getGenericReturnType();
}
// Now get the parametrized type of the generic.
if (!(type instanceof ParameterizedType))
throw new IllegalArgumentException("Given method's 1st param type is not parametrized generic: " + method);
ParameterizedType pType = (ParameterizedType) type;
final Type[] actualArgs = pType.getActualTypeArguments();
if (actualArgs.length == 0)
throw new IllegalArgumentException("Given method's 1st param type is not parametrized generic: " + method);
Type t = actualArgs[0];
if (t instanceof Class)
return (Class<?>) t;
if (t instanceof TypeVariable){
TypeVariable tv = (TypeVariable) actualArgs[0];
AnnotatedType[] annotatedBounds = tv.getAnnotatedBounds();///
GenericDeclaration genericDeclaration = tv.getGenericDeclaration();///
return (Class) tv.getAnnotatedBounds()[0].getType();
}
throw new IllegalArgumentException("Unknown kind of type: " + t.getTypeName());
}
변수에서 일반 매개 변수를 얻을 수 없습니다. 그러나 메소드 또는 필드 선언에서 다음을 수행 할 수 있습니다.
Method method = getClass().getDeclaredMethod("chill", List.class);
Type[] params = method.getGenericParameterTypes();
ParameterizedType firstParam = (ParameterizedType) params[0];
Type[] paramsOfFirstGeneric = firstParam.getActualTypeArguments();
이 코드 스 니펫을 읽는 것이 어려웠 기 때문에 2 개의 읽을 수있는 줄로 나눕니다.
// assuming that the Generic Type parameter is of type "T"
ParameterizedType p = (ParameterizedType) getClass().getGenericSuperclass();
Class<T> c =(Class<T>)p.getActualTypeArguments()[0];
내 메소드에 매개 변수가없는 Type 매개 변수의 인스턴스를 만들고 싶었습니다.
publc T getNewTypeInstance(){
ParameterizedType p = (ParameterizedType) getClass().getGenericSuperclass();
Class<T> c =(Class<T>)p.getActualTypeArguments()[0];
// for me i wanted to get the type to create an instance
// from the no-args default constructor
T t = null;
try{
t = c.newInstance();
}catch(Exception e){
// no default constructor available
}
return t;
}
또 다른 트릭이 있습니다. 일반적인 vararg 배열 사용
import java.util.ArrayList;
class TypedArrayList<E> extends ArrayList<E>
{
@SafeVarargs
public TypedArrayList (E... typeInfo)
{
// Get generic type at runtime ...
System.out.println (typeInfo.getClass().getComponentType().getTypeName());
}
}
public class GenericTest
{
public static void main (String[] args)
{
// No need to supply the dummy argument
ArrayList<Integer> ar1 = new TypedArrayList<> ();
ArrayList<String> ar2 = new TypedArrayList<> ();
ArrayList<?> ar3 = new TypedArrayList<> ();
}
}
많은 사람들이 getGenericSuperclass()
해결책에 기대어 있다는 것을 알았습니다 .
class RootGeneric<T> {
public Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
그러나이 솔루션은 오류가 발생하기 쉽습니다. 그것은 것입니다 하지 자손의 제네릭이있는 경우 제대로 작동합니다. 이걸 고려하세요:
class Foo<S> extends RootGeneric<Integer> {}
class Bar extends Foo<Double> {}
어떤 유형 Bar.persistentClass
이 있습니까? Class<Integer>
? 아뇨 Class<Double>
. 이것은 getClass()
항상 최상위 클래스를 반환하기 때문에 발생 합니다. Bar
이 경우 일반적인 수퍼 클래스는 Foo<Double>
입니다. 따라서 인수 유형은Double
.
실패하지 않는 안정적인 솔루션이 필요한 경우 두 가지를 제안 할 수 있습니다.
Guava
. 이 목적을 위해 만들어진 클래스가 com.google.common.reflect.TypeToken
있습니다. 모든 코너 케이스를 잘 처리하고 더 멋진 기능을 제공합니다. 단점은 추가적인 의존성입니다. 이 클래스를 사용하면 코드는 다음과 같이 간단하고 명확 해집니다.class RootGeneric<T> {
@SuppressWarnings("unchecked")
public final Class<T> persistentClass = (Class<T>) (new TypeToken<T>(getClass()) {}.getType());
}
abstract class RootGeneric<T> {
@SuppressWarnings("unchecked")
private Class<T> getTypeOfT() {
Class<T> type = null;
Class<?> iter = getClass();
while (iter.getSuperclass() != null) {
Class<?> next = iter.getSuperclass();
if (next != null && next.isAssignableFrom(RootGeneric.class)) {
type =
(Class<T>)
((ParameterizedType) iter.getGenericSuperclass()).getActualTypeArguments()[0];
break;
}
iter = next;
}
if (type == null) {
throw new ClassCastException("Cannot determine type of T");
}
return type;
}
}