런타임에 일반 유형의 클래스 가져 오기


508

어떻게하면 되나요?

public class GenericClass<T>
{
    public Type getMyType()
    {
        //How do I return the type of T?
    }
}

지금까지 시도한 모든 것은 항상 Object사용 된 특정 유형이 아닌 유형을 반환합니다 .


3
@SamuelVidal 이것은 .NET이 아니며 Java입니다.
Frontear

답변:


317

다른 사람들이 언급했듯이 특정 상황에서는 반사를 통해서만 가능합니다.

실제로 유형이 필요한 경우 일반적인 (유형 안전) 해결 방법 패턴입니다.

public class GenericClass<T> {

     private final Class<T> type;

     public GenericClass(Class<T> type) {
          this.type = type;
     }

     public Class<T> getMyType() {
         return this.type;
     }
}

58
이 답변이 마음에 들지만 인스턴스화하는 것은 약간 번거 롭습니다. GenericClass <AnotherClass> g = new GenericClass <AnotherClass> (AnotherClass.class);
Eliseo Ocampos

1
dao / factory / manager 접근 방식을 사용하면 훨씬 더 장황합니다. Foo foo1 = GetDao<Foo>(Foo.class).get(Foo.class, 1)
djmj

2
사실이지만 컨테이너 / 반사로 인스턴스화되는 상태 비 저장 원격 Bean과 같은 모든 경우에는 작동하지 않습니다.
djmj

6
내 이전 의견에 대한 후속 조치 인 것처럼 많은 고통을 겪은 후에이 답변을 사용했습니다.
Tomáš Zato-복원 모니카

18
일반적인 정적 팩토리 메소드를 제공하여 불필요한 참조를 해결할 수 있습니다. 같은 뭔가 public static <T> GenericClass<T> of(Class<T> type) {...}다음과 같은 호출 : GenericClass<String> var = GenericClass.of(String.class). 조금 더 멋지다.
Joeri Hendrickx

262

나는 이런 것을 보았다

private Class<T> persistentClass;

public Constructor() {
    this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
                            .getGenericSuperclass()).getActualTypeArguments()[0];
 }

에서 절전 GenericDataAccessObjects


84
이 기술은 직속 슈퍼 클래스에서 형식 매개 변수가 정의 된 곳에서 작동하지만 형식 매개 변수가 형식 계층의 다른 곳에 정의되어 있으면 실패합니다. 더 복잡한 경우를 처리하기 위해 TypeTools 와 같은 것을 사용할 수 있습니다. 이 문서에는보다 정교한 Generic DAO의 예가 포함되어 있습니다.
조나단

25
이것은 CLASS 가 일반 선언이있는 것을 구현 / 확장 할 때 사용 된 실제 유형 매개 변수 만 리턴하며 , INSTANCE 가 인스턴스화 될 때 사용 된 실제 유형 매개 변수는 리턴하지 않습니다 . 즉, CAN 에서 말할 class A implements Comparable<String>실제 유형 매개 변수가 String있지만 수없는 에서 말할 Set<String> a = new TreeSet<String>()실제 유형 매개 변수입니다 String. 실제로 다른 매개 변수에서 설명한 것처럼 형식 매개 변수 정보는 컴파일 후 "삭제"됩니다.
Siu Ching Pong -Asuka Kenji-

32
java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType이 답변을 받고 있습니다.
Tomáš Zato-복원 모니카

4
이 방법은 Class-MateJackson 사람들로부터 도 얻을 수 있습니다 . 나는 여기에 요점을 썼다 gist.github.com/yunspace/930d4d40a787a1f6a7d1
yunspace

5
@ TomášZato 단순히 위의 코드를 호출하면 동일한 예외가 반환되었습니다. 나는 그것이 조금 늦다는 것을 알고 있지만 어쨌든 내 경우에는 (Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()실제 유형 인수를 얻기 위해 전화 해야했습니다.
AndrewMcCoist

98

제네릭이되지 않습니다 구체화 실행시. 이는 정보가 런타임에 존재하지 않음을 의미합니다.

투어 드 힘이 자바에 제네릭을 이전 버전과의 호환성을 mantaining하는 동안 추가 (당신은 그것에 대해 독창적 인 논문을 볼 수 있습니다 : 과거에 대한 미래의 안전을 만들기 : 자바 프로그래밍 언어들이 generic을 추가 ).

이 주제에 대한 풍부한 문헌이 있으며 일부 사람들은 현재 상태에 불만을 가지고 있으며 , 일부 사람들은 실제로 그것이 미끼 이며 실제로 필요하지 않다고 말합니다. 두 링크를 모두 읽을 수 있습니다.


179
물론 우리는 불만족합니다. .NET은 훨씬 더 일반적인 처리 메커니즘을 가지고 있습니다
Pacerier

6
@Pacerier : 그러나 제네릭 제네릭만으로는 Java를 .NET 수준으로 가져올 수 없습니다. 값 유형에 대한 특수 코드는 제네릭 영역에서 .NET이 더 나은 이유에 대해 적어도 동일하게 중요합니다.
Joachim Sauer

@JoachimSauer, 예 값 유형. 나는 항상 자바 사람들을 원했다. Btw 특수 코드 란 무엇을 의미합니까?
Pacerier

3
@ spaaarky21 아니요, 컴파일하는 동안 일반 유형 매개 변수가 제거됩니다 (소위 "삭제", Google 할 수 있음). FrVaBe의 답변의 트릭은 수퍼 클래스의 유형 매개 변수가 정적으로 알려진 경우에만 작동합니다 (Johnathn의 첫 번째 주석 참조)
ewernli

1
Java 유형 삭제는 역사적인 디자인 결함입니다. 그것을 구현하기 위해 작성된 것보다 더 많은 코드가 해결되었습니다.
Abhijit Sarkar

68

구아바를 사용하십시오.

import com.google.common.reflect.TypeToken;
import java.lang.reflect.Type;

public abstract class GenericClass<T> {
  private final TypeToken<T> typeToken = new TypeToken<T>(getClass()) { };
  private final Type type = typeToken.getType(); // or getRawType() to return Class<? super T>

  public Type getType() {
    return type;
  }

  public static void main(String[] args) {
    GenericClass<String> example = new GenericClass<String>() { };
    System.out.println(example.getType()); // => class java.lang.String
  }
}

얼마 전 추상 클래스와 하위 클래스를 포함한 본격적인 예제를 여기에 게시했습니다 .

참고 :이 당신이 인스턴스화해야합니다 서브 클래스GenericClass제대로 유형 매개 변수를 바인딩 할 수 있도록합니다. 그렇지 않으면 유형을로 반환합니다 T.


3
빈 익명 서브 클래스를 만듭니다 (끝에 두 개의 중괄호 참조). 이것은 리플렉션을 사용하여 Java의 런타임 유형 삭제와 싸 웁니다. 당신은 자세한 내용은 여기를 배울 수 있습니다 : code.google.com/p/guava-libraries/wiki/ReflectionExplained
코디 A. 레이

3
@ CodyA.Ray 코드가를 던집니다 java.lang.IllegalArgumentException: class com.google.common.reflect.TypeToken isn't parameterized. 그래서 줄 new TypeToken(getClass()) { }을으로 변경 했습니다 new TypeToken<T>(getClass()) { }. 이제 코드가 제대로 실행되지만 Type은 여전히 ​​'T'입니다. 참조 : gist.github.com/m-manu/9cda9d8f9d53bead2035
Manu Manjunath

3
@Dominik 복사하여 붙여 넣기하여 테스트 할 수있는 업데이트 된 예를 참조하십시오. 또한 하위 클래스를 인스턴스화해야 함을 나타내는 메모를 추가했습니다 (그림 참조). 일반적인 에티켓 조언으로, "소중한 사고"의 포스터를 비난하기 전에 링크 된 기사 및 관련 javadoc을 읽으십시오. 비슷한 프로덕션 코드를 여러 번 사용했습니다. 내가 설명하는 Guava 헬퍼는이 정확한 사용 사례를 대상으로하며 javadocs는이 질문에 거의 정확한 답변을 보여줍니다. docs.guava-libraries.googlecode.com/git/javadoc/com/google/…
Cody A. Ray

1
이 답변은 Java8에서 잘 작동합니다. 다양한 유형을 방출하는 인터페이스와 팩토리 클래스가 있으며 사용자가 유형을 쉽게 결정할 수 있기를 원했습니다. 내 인터페이스에 기본 방법을 추가했습니다. default Type getParameterType() { final TypeToken<T> typeToken = new TypeToken<T>(getClass()) {}; final Type type = typeToken.getType(); return type; }
Richard Sand

2
@ CodyA.Ray 이것은의 서브 클래스에서만 작동 하기 때문에 잘못된 사용법이 컴파일되지 않도록 GenericClass해당 클래스를 만들어야합니다 abstract.
마틴

37

물론 넌 할 수있어.

Java는 이전 버전과의 호환성을 위해 런타임에 정보를 사용 하지 않습니다 . 그러나 정보는 실제로 메타 데이터로 존재 하며 리플렉션을 통해 액세스 할 수 있습니다 (그러나 여전히 유형 확인에는 사용되지 않습니다).

공식 API에서 :

http://download.oracle.com/javase/6/docs/api/java/lang/reflect/ParameterizedType.html#getActualTypeArguments%28%29

그러나 귀하의 시나리오에서는 리플렉션을 사용하지 않습니다. 개인적으로 프레임 워크 코드에 사용하는 경향이 있습니다. 귀하의 경우 유형을 생성자 매개 변수로 추가합니다.


10
getActualTypeArguments는 직접 클래스에 대한 유형 인수 만 리턴합니다. 계층 구조의 어느 곳에서나 T를 매개 변수화 할 수있는 복잡한 유형 계층 구조가있는 경우 그것이 무엇인지 파악하기 위해 약간의 작업을 수행해야합니다. 이것은 TypeTools의 기능 입니다.
조나단

36

Java 제네릭은 대부분 컴파일 시간이므로 유형 정보가 런타임에 손실됩니다.

class GenericCls<T>
{
    T t;
}

같은 것으로 컴파일됩니다

class GenericCls
{
   Object o;
}

런타임에 유형 정보를 얻으려면 ctor의 인수로 추가해야합니다.

class GenericCls<T>
{
     private Class<T> type;
     public GenericCls(Class<T> cls)
     {
        type= cls;
     }
     Class<T> getType(){return type;}
}

예:

GenericCls<?> instance = new GenericCls<String>(String.class);
assert instance.getType() == String.class;

9
private final Class<T> type;
naXa

배열 유형을 생성하는 방법 :Type t = //String[]
Pawel Cioch

@PawelCioch java.lang.reflect.Array.newInstance (요소 유형, 길이); 이것이 도움이되기를 바랍니다 (javadoc은 docs.oracle.com/javase/8/docs/api/java/lang/reflect/… 에서 찾을 수 있습니다 )
josefx

@PawelCioch는 생성 된 배열에서 유형을 가져 오기 위해 .getClass ()를 놓쳤습니다. 배열 클래스를 얻는 직접적인 방법은없는 것 같습니다. 대부분의 Java 컬렉션은 Object []를 대신 사용합니다.
josefx

23
public abstract class AbstractDao<T>
{
    private final Class<T> persistentClass;

    public AbstractDao()
    {
        this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
    }
}

3
이 답변은 묻는 질문에 맞는 해결책이므로이 답변을지지하고 있습니다. 그러나 둘 이상의 Generic 클래스를 사용하는 나와 같은 클래스 계층 구조에서 위로 탐색하려는 경우 작동하지 않습니다. 실제 클래스 대신 java.lang.object를 얻을 수 있기 때문입니다.
KMC

3
일반 유형을 보유하고있는 클래스가 ABSTRACT 경우에만이 솔루션이 작동 있습니다
BabaNew

(11) 자바에서 내 추상 클래스와 함께 작동하지 않았다
JRA_TLL에게

@JRA_TLL 당신은 분명히 뭔가 잘못했습니다. 방금 Java 12와 함께 사용했으며 매력처럼 작동합니다.
Googie

뷰 계층 구조에서 위로 이동하려면 genericSuperclass를 Class <*>로 캐스팅하고 genericSuperclass를 얻을 수 있습니다. 루프에서 바람직하다.
fupduck

21

나는 다음과 같은 접근 방식을 사용했습니다.

public class A<T> {

    protected Class<T> clazz;

    public A() {
        this.clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public Class<T> getClazz() {
        return clazz;
    }
}

public class B extends A<C> {
   /* ... */
    public void anything() {
       // here I may use getClazz();
    }
}

9
"샘플"메인 "java.lang.ClassCastException에서 예외가 발생했습니다.이 샘플 코드를 사용하여 java.lang.Class를 java.lang.reflect.ParameterizedType으로 캐스트 할 수 없습니다"
yuyang

16

Java가 컴파일 할 때 유형 삭제를 사용하므로 코드가 프리 제너릭으로 작성된 응용 프로그램 및 라이브러리와 호환됩니다.

Oracle Docs에서 :

타입 소거

컴파일시에 더 엄격한 유형 검사를 제공하고 일반 프로그래밍을 지원하기 위해 Java 언어에 제네릭이 도입되었습니다. 제네릭을 구현하기 위해 Java 컴파일러는 유형 삭제를 다음에 적용합니다.

제네릭 형식의 모든 형식 매개 변수를 해당 경계 또는 형식 매개 변수가 제한되지 않은 경우 Object로 바꿉니다. 따라서 생성 된 바이트 코드에는 일반 클래스, 인터페이스 및 메소드 만 포함됩니다. 유형 안전을 유지하기 위해 필요한 경우 유형 캐스트를 삽입하십시오. 확장 된 제네릭 형식에서 다형성을 유지하기 위해 브리지 방법을 생성합니다. 유형 삭제는 매개 변수화 된 유형에 대해 새 클래스가 작성되지 않도록합니다. 결과적으로 제네릭은 런타임 오버 헤드를 발생시키지 않습니다.

http://docs.oracle.com/javase/tutorial/java/generics/erasure.html


up, 불가능 해 Java가 작동하려면 통일 된 제네릭이 필요합니다.
Henning

15

기사에서 Ian Robertson 이 설명한 기술이 저에게 효과적입니다.

짧고 더러운 예를 들면 다음과 같습니다.

 public abstract class AbstractDAO<T extends EntityInterface, U extends QueryCriteria, V>
 {
    /**
     * Method returns class implementing EntityInterface which was used in class
     * extending AbstractDAO
     *
     * @return Class<T extends EntityInterface>
     */
    public Class<T> returnedClass()
    {
        return (Class<T>) getTypeArguments(AbstractDAO.class, getClass()).get(0);
    }

    /**
     * Get the underlying class for a type, or null if the type is a variable
     * type.
     *
     * @param type the type
     * @return the underlying class
     */
    public static Class<?> getClass(Type type)
    {
        if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof ParameterizedType) {
            return getClass(((ParameterizedType) type).getRawType());
        } else if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            Class<?> componentClass = getClass(componentType);
            if (componentClass != null) {
                return Array.newInstance(componentClass, 0).getClass();
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * Get the actual type arguments a child class has used to extend a generic
     * base class.
     *
     * @param baseClass the base class
     * @param childClass the child class
     * @return a list of the raw classes for the actual type arguments.
     */
    public static <T> List<Class<?>> getTypeArguments(
            Class<T> baseClass, Class<? extends T> childClass)
    {
        Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
        Type type = childClass;
        // start walking up the inheritance hierarchy until we hit baseClass
        while (!getClass(type).equals(baseClass)) {
            if (type instanceof Class) {
                // there is no useful information for us in raw types, so just keep going.
                type = ((Class) type).getGenericSuperclass();
            } else {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                Class<?> rawType = (Class) parameterizedType.getRawType();

                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
                for (int i = 0; i < actualTypeArguments.length; i++) {
                    resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
                }

                if (!rawType.equals(baseClass)) {
                    type = rawType.getGenericSuperclass();
                }
            }
        }

        // finally, for each actual type argument provided to baseClass, determine (if possible)
        // the raw class for that type argument.
        Type[] actualTypeArguments;
        if (type instanceof Class) {
            actualTypeArguments = ((Class) type).getTypeParameters();
        } else {
            actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
        }
        List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
        // resolve types by chasing down type variables.
        for (Type baseType : actualTypeArguments) {
            while (resolvedTypes.containsKey(baseType)) {
                baseType = resolvedTypes.get(baseType);
            }
            typeArgumentsAsClasses.add(getClass(baseType));
        }
        return typeArgumentsAsClasses;
    }
  }

3
이 코드의 어느 특정 행에서 실제 런타임 유형 매개 변수를 읽습니까?
Ivan Matavulj

여기? Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Ondrej Bozek

9

또 다른 우아한 해결책이 있다고 생각합니다.

당신이하고 싶은 것은 일반 클래스 매개 변수의 타입을 concerete 클래스에서 수퍼 클래스로 (안전하게) 전달하는 것입니다.

클래스에서 클래스 유형을 "메타 데이터"로 생각할 수 있으면 런타임시 메타 데이터를 인코딩하는 Java 메소드 인 주석이 제안됩니다.

먼저 다음 행을 따라 사용자 정의 주석을 정의하십시오.

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
    Class entityClass();
}

그런 다음 서브 클래스에 주석을 추가해야합니다.

@EntityAnnotation(entityClass =  PassedGenericType.class)
public class Subclass<PassedGenericType> {...}

그런 다음이 코드를 사용하여 기본 클래스에서 클래스 유형을 가져올 수 있습니다.

import org.springframework.core.annotation.AnnotationUtils;
.
.
.

private Class getGenericParameterType() {
    final Class aClass = this.getClass();
    EntityAnnotation ne = 
         AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class);

    return ne.entityClass();
}

이 접근법의 일부 제한 사항은 다음과 같습니다.

  1. PassedGenericTypeDRY 이외의 장소가 아닌 두 곳에서 일반 유형 ( )을 지정합니다 .
  2. 구체적인 서브 클래스를 수정할 수있는 경우에만 가능합니다.

예, 건조하지는 않지만 위에서 제안한 확장 방식보다 깨끗합니다. 나는 그것을 좋아. 감사합니다
agodinhost

8

이것은 내 솔루션입니다.

import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;

public class GenericClass<T extends String> {

  public static void main(String[] args) {
     for (TypeVariable typeParam : GenericClass.class.getTypeParameters()) {
      System.out.println(typeParam.getName());
      for (Type bound : typeParam.getBounds()) {
         System.out.println(bound);
      }
    }
  }
}

10
이것은이 질문에 대한 답이 아닙니다.
Adam Arold

1
내 코드는 질문에 대한 정확한 해결책이 아닙니다. 클래스의 일반 유형 매개 변수를 반환하지만 실제 T 유형은 반환하지 않습니다. 그러나 질문에 걸려 넘어지고 내 솔루션을 찾는 다른 사람들에게 도움이 될 수 있습니다.
Matthias M

1
getClass (). getGenericSuperclass ()는 동일한 효과를 얻습니다.
Gorky

5

여기에 해결책이 있습니다 !!!

@SuppressWarnings("unchecked")
    private Class<T> getGenericTypeClass() {
        try {
            String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
            Class<?> clazz = Class.forName(className);
            return (Class<T>) clazz;
        } catch (Exception e) {
            throw new IllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");
        }
    } 

참고 : 수퍼 클래스로만 사용할 수 있습니다
. 1. 형식화 된 클래스 ( Child extends Generic<Integer>) 로 확장해야합니다.
또는

2. 익명 구현 ( new Generic<Integer>() {};) 으로 만들어야합니다.


4

당신은 할 수 없습니다. 클래스에 유형 T의 멤버 변수를 클래스에 추가하면 (초기화하지 않아도)이를 사용하여 유형을 복구 할 수 있습니다.


2
알았어 당신은 생성자에서 그것을 초기화해야합니다.
andrewmu

4

한두 가지 방법으로 한두 번 사용해야했습니다.

public abstract class GenericClass<T>{
    public abstract Class<T> getMyType();
}

와 함께

public class SpecificClass extends GenericClass<String>{

    @Override
    public Class<String> getMyType(){
        return String.class;
    }
}

4
이것은 기술적으로 효과가 있지만 일반적인 경우를 해결하지 못하며 원래 포스터가 나온 것입니다.
ggb667

이것은 원래의 포스터가 명확하지 않은 것처럼 투표권을 가질 자격이 없습니다. 이 답변을 제공하는 디자인 패턴 않는 일을하고 구현하기 쉬운, 일반 클래스가 추상적하기에 적합 제공.
VirtualMichael

4

이 택시에 대한 간단한 해결책은 다음과 같습니다.

public class GenericDemo<T>{
    private T type;

    GenericDemo(T t)
    {
        this.type = t;
    }

    public String getType()
    {
        return this.type.getClass().getName();
    }

    public static void main(String[] args)
    {
        GenericDemo<Integer> obj = new  GenericDemo<Integer>(5);
        System.out.println("Type: "+ obj.getType());
    }
}

3

여기에 대한 답변 중 일부를 완료하려면 재귀의 도움으로 계층 구조의 높이에 관계없이 MyGenericClass의 ParametrizedType을 가져와야했습니다.

private Class<T> getGenericTypeClass() {
        return (Class<T>) (getParametrizedType(getClass())).getActualTypeArguments()[0];
}

private static ParameterizedType getParametrizedType(Class clazz){
    if(clazz.getSuperclass().equals(MyGenericClass.class)){ // check that we are at the top of the hierarchy
        return (ParameterizedType) clazz.getGenericSuperclass();
    } else {
        return getParametrizedType(clazz.getSuperclass());
    }
}

1

내 요령은 다음과 같습니다.

public class Main {

    public static void main(String[] args) throws Exception {

        System.out.println(Main.<String> getClazz());

    }

    static <T> Class getClazz(T... param) {

        return param.getClass().getComponentType();
    }

}

1
참고 : 유형 변수 인 경우 에는 작동하지 않습니다T . 경우 T타입 변수의 가변 인자는 소거의 어레이를 생성한다 T. 예를 들어 http://ideone.com/DIPNwd를 참조하십시오 .
Radiodef

1
"Object"
yahya

당신은 다른 질문에 대답하려고 할지도 모릅니다 🤔
Khan

1

제네릭 형식을 사용하여 변수를 저장하는 경우 getClassType 메서드를 추가하여 다음과 같이이 문제를 쉽게 해결할 수 있습니다.

public class Constant<T> {
  private T value;

  @SuppressWarnings("unchecked")
  public Class<T> getClassType () {
    return ((Class<T>) value.getClass());
  }
}

제공된 클래스 객체를 나중에 다음과 같이 주어진 클래스의 인스턴스인지 확인합니다.

Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
    Constant<Integer> integerConstant = (Constant<Integer>)constant;
    Integer value = integerConstant.getValue();
    // ...
}

2
불행히도 문제가됩니다. 우선, 경우에 무엇 value이다 null? 둘째 value, 하위 클래스가 T무엇입니까? Constant<Number> c = new Constant<Number>(new Integer(0)); Class<Number> n = c.getClassType();반환 Integer.class해야 할 때 반환 Number.class합니다. 반환하는 것이 더 정확합니다 Class<? extends T>. Integer.class A는 Class<? extends Number> 아니지만 Class<Number>.
Radiodef

1

여기 내 해결책이 있습니다

public class GenericClass<T>
{
    private Class<T> realType;

    public GenericClass() {
        findTypeArguments(getClass());
    }

    private void findTypeArguments(Type t) {
        if (t instanceof ParameterizedType) {
            Type[] typeArgs = ((ParameterizedType) t).getActualTypeArguments();
            realType = (Class<T>) typeArgs[0];
        } else {
            Class c = (Class) t;
            findTypeArguments(c.getGenericSuperclass());
        }
    }

    public Type getMyType()
    {
        // How do I return the type of T? (your question)
        return realType;
    }
}

클래스 계층 구조의 수준에 관계없이이 솔루션은 다음과 같이 작동합니다.

public class FirstLevelChild<T> extends GenericClass<T> {

}

public class SecondLevelChild extends FirstLevelChild<String> {

}

이 경우 getMyType () = java.lang.String


이것은 코드가 클래스 <T>에 입력 은밀한에 실패한 외에 java.lang.String의하지 T 반환 T.의 종류 반환하지 않습니다
Mohy Eldeen

여기 내가 만든 온라인 샘플이 있습니다. 컴파일 및 실행을 클릭하면 결과를 얻을 수 있습니다. tutorialspoint.com/…
Lin Yu Cheng

WildFly Weld CDI가 대체 방법을 위반했을 때 저에게 효과적입니다.
Andre

내가 가지고Exception in thread "main" java.lang.NullPointerException at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.<init>(Main.java:43) at Main.main(Main.java:61)
유양

0
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
    return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}

0

여기 내 해결책이 있습니다. 예제에서 설명해야합니다. 유일한 요구 사항은 서브 클래스가 오브젝트가 아닌 일반 유형을 설정해야한다는 것입니다.

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;

public class TypeUtils {

    /*** EXAMPLES ***/

    public static class Class1<A, B, C> {

        public A someA;
        public B someB;
        public C someC;

        public Class<?> getAType() {
            return getTypeParameterType(this.getClass(), Class1.class, 0);
        }

        public Class<?> getCType() {
            return getTypeParameterType(this.getClass(), Class1.class, 2);
        }
    }

    public static class Class2<D, A, B, E, C> extends Class1<A, B, C> {

        public B someB;
        public D someD;
        public E someE;
    }

    public static class Class3<E, C> extends Class2<String, Integer, Double, E, C> {

        public E someE;
    }

    public static class Class4 extends Class3<Boolean, Long> {

    }

    public static void test() throws NoSuchFieldException {

        Class4 class4 = new Class4();
        Class<?> typeA = class4.getAType(); // typeA = Integer
        Class<?> typeC = class4.getCType(); // typeC = Long

        Field fieldSomeA = class4.getClass().getField("someA");
        Class<?> typeSomeA = TypeUtils.getFieldType(class4.getClass(), fieldSomeA); // typeSomeA = Integer

        Field fieldSomeE = class4.getClass().getField("someE");
        Class<?> typeSomeE = TypeUtils.getFieldType(class4.getClass(), fieldSomeE); // typeSomeE = Boolean


    }

    /*** UTILS ***/

    public static Class<?> getTypeVariableType(Class<?> subClass, TypeVariable<?> typeVariable) {
        Map<TypeVariable<?>, Type> subMap = new HashMap<>();
        Class<?> superClass;
        while ((superClass = subClass.getSuperclass()) != null) {

            Map<TypeVariable<?>, Type> superMap = new HashMap<>();
            Type superGeneric = subClass.getGenericSuperclass();
            if (superGeneric instanceof ParameterizedType) {

                TypeVariable<?>[] typeParams = superClass.getTypeParameters();
                Type[] actualTypeArgs = ((ParameterizedType) superGeneric).getActualTypeArguments();

                for (int i = 0; i < typeParams.length; i++) {
                    Type actualType = actualTypeArgs[i];
                    if (actualType instanceof TypeVariable) {
                        actualType = subMap.get(actualType);
                    }
                    if (typeVariable == typeParams[i]) return (Class<?>) actualType;
                    superMap.put(typeParams[i], actualType);
                }
            }
            subClass = superClass;
            subMap = superMap;
        }
        return null;
    }

    public static Class<?> getTypeParameterType(Class<?> subClass, Class<?> superClass, int typeParameterIndex) {
        return TypeUtils.getTypeVariableType(subClass, superClass.getTypeParameters()[typeParameterIndex]);
    }

    public static Class<?> getFieldType(Class<?> clazz, AccessibleObject element) {
        Class<?> type = null;
        Type genericType = null;

        if (element instanceof Field) {
            type = ((Field) element).getType();
            genericType = ((Field) element).getGenericType();
        } else if (element instanceof Method) {
            type = ((Method) element).getReturnType();
            genericType = ((Method) element).getGenericReturnType();
        }

        if (genericType instanceof TypeVariable) {
            Class<?> typeVariableType = TypeUtils.getTypeVariableType(clazz, (TypeVariable) genericType);
            if (typeVariableType != null) {
                type = typeVariableType;
            }
        }

        return type;
    }

}

-1

나는 @Moesio 위에서와 동일했지만 Kotlin에서는 다음과 같이 할 수 있습니다.

class A<T : SomeClass>() {

    var someClassType : T

    init(){
    this.someClassType = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<T>
    }

}

-4

누군가에게 유용 할 수 있습니다. java.lang.ref.WeakReference를 사용할 수 있습니다. 이 방법:

class SomeClass<N>{
  WeakReference<N> variableToGetTypeFrom;

  N getType(){
    return variableToGetTypeFrom.get();
  }
}

이 수업은 어떻게 사용 되나요? 왜 WeakReference? 코드가 아니라 답변에 대한 설명을 입력하십시오.
Abhijit Sarkar

따라서 SomeClass<MyClass> 인스턴스 가 있으면 인스턴스를 인스턴스화 SomeClass하고 호출 할 수 getType있으며 런타임은입니다 MyClass.
TheLetch

그러나 왜 WeakReference? 당신이 말한 것은 대부분의 다른 답변과 다르지 않습니다.
Abhijit 사카

먼저 나의 접근 방식은 짧은 (적은 코드)는 두 번째 약한 참조는 마무리가 만든되는 것을 저지 할 것은 없습니다, 나는 그것이 반사를 사용하지 않습니다 알고 멀리로, 따라서 그것은 빨리이다
TheLetch

이것은이, 참고하시기 바랍니다, 당신이 래퍼의 문자 그대로 어떤 종류로 할 수있는 해당 유형의 개체를 반환 아무것도의 유형을하지 않습니다 ( AtomicReference, List, Set).
Frontear

-5

나는 이것이 간단하고 이해하기 쉬운 해결책이라는 것을 알았습니다.

public class GenericClass<T> {

    private Class classForT(T...t) {
        return t.getClass().getComponentType();
    }

    public static void main(String[] args) {
        GenericClass<String> g = new GenericClass<String>();

        System.out.println(g.classForT());
        System.out.println(String.class);
    }
}

설명하십시오 (T...t). (그래서이 코드가 작동하지 않는 것입니다.)
Christopher Schneider
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.