Programing

리플렉션을 사용하여 Java에서 일반 매개 변수 유형 가져 오기

lottogame 2020. 7. 4. 10:39
반응형

리플렉션을 사용하여 Java에서 일반 매개 변수 유형 가져 오기


일반 매개 변수의 유형을 얻을 수 있습니까?

예를 들면 :

public final class Voodoo {
    public static void chill(List<?> aListWithTypeSpiderMan) {
        // Here I'd like to get the Class-Object 'SpiderMan'
        Class typeOfTheList = ???;
    }

    public static void main(String... args) {
        chill(new ArrayList<SpiderMan>());
    }
}

하나의 구조, 나는 한 번 넘어졌다

Class<T> persistentClass = (Class<T>)
   ((ParameterizedType)getClass().getGenericSuperclass())
      .getActualTypeArguments()[0];

불행히도 완전히 이해하지 못하는 주변에 반성 마법이있는 것 같습니다 ... 미안합니다.


@DerMike의 답변을 세분화하여 설명하려고합니다.

먼저, 유형 삭제가 JDK 가 런타임시 유형 정보를 제거 한다는 것을 의미하지는 않습니다 . 컴파일 타임 유형 검사 및 런타임 유형 호환성이 동일한 언어로 공존 할 수 있도록하는 방법입니다. 이 코드 블록에서 알 수 있듯이 JDK는 지워진 유형 정보를 유지합니다. 이는 확인 된 캐스트 및 항목과 관련이 없습니다.

둘째, 이것은 검사중인 콘크리트 유형에서 계층의 정확히 한 수준 위의 일반 클래스에 일반 유형 정보를 제공합니다. 즉, 일반 유형 매개 변수가있는 추상 상위 클래스는 자체의 구체적인 구현을 위해 유형 매개 변수에 해당하는 구체적인 유형을 찾을 수 있습니다. 그것 으로부터 직접 상속받습니다. 이 클래스가 비 추상적이고 인스턴스화되었거나 구체적인 구현이 두 수준 아래로 내려간 경우에는 작동하지 않습니다 (조금의 약간의 지미로 인해 하나 이상의 클래스 또는 가장 낮은 클래스까지 미리 정해진 수의 레벨에 적용 할 수 있음) X 제네릭 형식 매개 변수 등).

어쨌든, 설명에. 다음은 쉽게 참조 할 수 있도록 코드를 다시 한 줄로 분리 한 것입니다.

1 # 클래스 genericParameter0OfThisClass = 
2 # (클래스)
3 # ((매개 변수화 된 유형)
4 # getClass ()
5 # .getGenericSuperclass ())
6 # .getActualTypeArguments () [0];

이 코드를 포함하는 제네릭 형식의 추상 클래스 인 'us'를 보자. 이것을 대략 내부에서 읽는다.

  • 4 행은 현재 구체적 클래스의 Class 인스턴스를 가져옵니다. 이것은 우리의 직계 후손의 구체적인 유형을 식별합니다.
  • 5 행은 해당 클래스의 수퍼 타입을 유형으로 가져옵니다. 이것은 우리입니다. 우리는 파라 메트릭 타입이기 때문에 안전하게 ParameterizedType (라인 3)으로 캐스팅 할 수 있습니다. 핵심은 Java가이 Type 개체를 결정할 때 자식에있는 형식 정보를 사용하여 형식 정보를 새 ParameterizedType 인스턴스의 형식 매개 변수와 연결한다는 것입니다. 이제 제네릭의 구체적인 유형에 액세스 할 수 있습니다.
  • 6 행은 클래스 코드에 선언 된 순서대로 제네릭에 매핑 된 형식의 배열을 가져옵니다. 이 예에서는 첫 번째 매개 변수를 뽑습니다. 이것은 유형으로 돌아옵니다.
  • 2 행은 클래스에 반환 된 최종 Type을 캐스팅합니다. 우리는 제네릭 형식 매개 변수가 취할 수있는 유형을 알고 있으며 모두 유형이 될 것임을 확인할 수 있기 때문에 안전합니다 (Java에서는 Class 인스턴스가없는 일반 매개 변수를 얻는 방법을 잘 모르겠습니다) 실제로 관련).

... 그 정도입니다. 그래서 우리는 우리 자신의 구체적인 구현에서 타입 정보를 다시 우리 자신에게 푸시하고 그것을 사용하여 클래스 핸들에 액세스합니다. 우리는 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>(). 성가신 클래스 (가능한 경우)를 사용하면 컴파일러가 SpiderMantype 매개 변수에 지정된 형식 인수에 대한 정보를 유지합니다 List<?>. oil!


유형 삭제 때문에 목록의 유형을 알 수있는 유일한 방법은 유형을 메소드에 매개 변수로 전달하는 것입니다.

public class Main {

    public static void main(String[] args) {
        doStuff(new LinkedList<String>(), String.class);

    }

    public static <E> void doStuff(List<E> list, Class<E> clazz) {

    }

}

Nope, that is not possible. Due to downwards compatibility issues, Java's generics are based on type erasure, i.a. at runtime, all you have is a non-generic List object. There is some information about type parameters at runtime, but it resides in class definitions (i.e. you can ask "what generic type does this field's definition use?"), not in object instances.


Appendix to @DerMike's answer for getting the generic parameter of a parameterized interface (using #getGenericInterfaces() method inside a Java-8 default method to avoid duplication):

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
}

As pointed out by @bertolami, it's not possible to us a variable type and get its future value (the content of typeOfList variable).

Nevertheless, you can pass the class as parameter on it like this:

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 );
    }
}

That's more or less what Google does when you have to pass a class variable to the constructor of ActivityInstrumentationTestCase2.


No it isn't possible.

You can get a generic type of a field given a class is the only exception to that rule and even that's a bit of a hack.

See Knowing type of generic in Java for an example of that.


This is impossible because generics in Java are only considered at compile time. Thus, the Java generics are just some kind of pre-processor. However you can get the actual class of the members of the list.


You can get the type of a generic parameter with reflection like in this example that I found here:

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());
    }
}

I've coded this for methods which expect to accept or return Iterable<?...>. Here is the code:

/**
 * 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());
}

You cannot get a generic parameter from a variable. But you can from a method or field declaration:

Method method = getClass().getDeclaredMethod("chill", List.class);
Type[] params = method.getGenericParameterTypes();
ParameterizedType firstParam = (ParameterizedType) params[0];
Type[] paramsOfFirstGeneric = firstParam.getActualTypeArguments();

Just for me reading this snippet of code was hard, I just divided it into 2 readable lines :

// assuming that the Generic Type parameter is of type "T"
ParameterizedType p = (ParameterizedType) getClass().getGenericSuperclass();
Class<T> c =(Class<T>)p.getActualTypeArguments()[0];

I wanted to create an instance of the Type parameter without having any parameters to my method :

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;
}

The quick answer the the Question is no you can't, because of Java generic type erasure.

The longer answer would be that if you have created your list like this:

new ArrayList<SpideMan>(){}

Then in this case the generic type is preserved in the generic superclass of the new anonymous class above.

Not that I recommend doing this with lists, but it is a listener implementation:

new Listener<Type>() { public void doSomething(Type t){...}}

And since extrapolating the generic types of super classes and super interfaces change between JVMs, the generic solution is not as straight forward as some answers might suggest.

Here is now I did it.


Use:

Class<?> typeOfTheList = aListWithTypeSpiderMan.toArray().getClass().getComponentType();

참고URL : https://stackoverflow.com/questions/1901164/get-type-of-a-generic-parameter-in-java-with-reflection

반응형