development

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

big-blog 2020. 2. 15. 23:09
반응형

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


어떻게하면 되나요?

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

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


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

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

public class GenericClass<T> {

     private final Class<T> type;

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

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

나는 이런 것을 보았다

private Class<T> persistentClass;

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

에서 절전 GenericDataAccessObjects


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

이전 버전과의 호환성을 유지하면서 Java에 제네릭을 추가하는 것은 둘러보기의 힘이되었습니다 (이에 대한 중요한 논문을 볼 수 있습니다 : 과거를 안전하게 보호 : Java 프로그래밍 언어에 일반성을 추가 ).

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


구아바를 사용하십시오.

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.


물론 넌 할 수있어.

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

공식 API에서 :

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

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


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;

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

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

기사에서 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;
    }
  }

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

Oracle Docs에서 :

타입 소거

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

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

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


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

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

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

당신이하고 싶은 것은 일반 클래스 매개 변수의 타입을 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. 구체적인 서브 클래스를 수정할 수있는 경우에만 가능합니다.

이것은 내 솔루션입니다.

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

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

@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>() {};) 으로 만들어야합니다.


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


한 가지 방법이 있는데 한두 번 사용해야했습니다.

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

와 함께

public class SpecificClass extends GenericClass<String>{

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

여기에 대한 답변 중 일부를 완료하려면 재귀의 도움으로 계층 구조의 높이에 관계없이 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());
    }
}

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

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

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

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

}

제네릭 형식을 사용하여 변수를 저장하는 경우 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();
    // ...
}

여기 내 해결책이 있습니다

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


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

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

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

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

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

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

참고 URL : https://stackoverflow.com/questions/3403909/get-generic-type-of-class-at-runtime



반응형