[java] 리플렉션을 사용하여 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) {

    }

}


답변

매개 변수화 된 인터페이스의 일반 매개 변수를 가져 오는 @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
}


답변

아니, 그건 불가능 해 호환성 호환성 문제로 인해 Java의 제네릭은 유형 삭제 (즉 , 런타임시)를 기반으로 List합니다. 런타임에 형식 매개 변수에 대한 정보가 있지만 개체 정의가 아니라 클래스 정의에 있습니다 (예 : ” 이 필드의 정의에서 어떤 일반 형식을 사용합니까? “).