programing tip

제네릭 유형 T의 클래스 인스턴스를 얻는 방법

itbloger 2020. 10. 2. 21:46
반응형

제네릭 유형 T의 클래스 인스턴스를 얻는 방법


제네릭 클래스가 Foo<T>있습니다. 의 메서드에서 FooT 유형의 클래스 인스턴스를 가져오고 싶지만을 호출 할 수 없습니다 T.class.

사용하여 주변을 돌아 다니는 선호하는 방법은 무엇입니까 T.class?


짧은 대답은 Java에서 제네릭 유형 매개 변수의 런타임 유형을 찾을 수있는 방법이 없다는 것입니다. 자세한 내용 Java Tutorial 에서 유형 삭제에 대한 장을 읽는 것이 좋습니다 .

이것에 대한 대중적인 해결책 Class은 타입 매개 변수의를 제네릭 타입의 생성자에 전달하는 것 입니다.

class Foo<T> {
    final Class<T> typeParameterClass;

    public Foo(Class<T> typeParameterClass) {
        this.typeParameterClass = typeParameterClass;
    }

    public void bar() {
        // you can access the typeParameterClass here and do whatever you like
    }
}

클래스 경로에 추가 종속성을 추가하지 않고 직접 수행 할 수있는 방법을 찾고있었습니다. 일부 조사 후 나는 것을 발견 하다 만큼 당신이 일반 슈퍼를 가지고 가능. 제네릭 레이어 상위 유형을 사용하는 DAO 레이어 로 작업 할 때 괜찮 았습니다. 이것이 귀하의 시나리오에 맞다면 가장 가까운 접근 방식 IMHO입니다.

내가 만난 대부분의 제네릭 유스 케이스에는 List<T>for ArrayList<T>또는 GenericDAO<T>for DAO<T>의 일반적인 수퍼 유형이 있습니다 .

순수 자바 솔루션

Java에서 런타임에 제네릭 유형 액세스 기사에서는 순수 Java를 사용하여 수행하는 방법을 설명합니다.

스프링 솔루션

내 프로젝트가 사용하던 봄 유형을 찾기위한 편리한 유틸리티 방법이 있기 때문에 더 나은이다. 이것은 가장 깔끔해 보이므로 가장 좋은 접근 방식입니다. Spring을 사용하지 않았다면 자신 만의 유틸리티 메소드를 작성할 수있을 것 같습니다.

import org.springframework.core.GenericTypeResolver;

public abstract class AbstractHibernateDao<T extends DomainObject> implements DataAccessObject<T>
{

    @Autowired
    private SessionFactory sessionFactory;

    private final Class<T> genericType;

    private final String RECORD_COUNT_HQL;
    private final String FIND_ALL_HQL;

    @SuppressWarnings("unchecked")
    public AbstractHibernateDao()
    {
        this.genericType = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), AbstractHibernateDao.class);
        this.RECORD_COUNT_HQL = "select count(*) from " + this.genericType.getName();
        this.FIND_ALL_HQL = "from " + this.genericType.getName() + " t ";
    }

그러나 작은 허점이 있습니다 Foo. 클래스를 추상으로 정의하면 . 즉, 클래스를 다음과 같이 인스턴스화해야합니다.

Foo<MyType> myFoo = new Foo<MyType>(){};

(끝에 이중 중괄호가 있습니다.)

이제 T런타임에 유형을 검색 할 수 있습니다 .

Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];

그러나 이것은 mySuperclass실제로 최종 유형을 정의하는 클래스 정의의 수퍼 클래스 여야합니다 T.

또한 그다지 우아하지는 않지만 선호 new Foo<MyType>(){}하거나 new Foo<MyType>(MyType.class);코드에서 결정 해야합니다.


예를 들면 :

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.NoSuchElementException;

/**
 * Captures and silently ignores stack exceptions upon popping.
 */
public abstract class SilentStack<E> extends ArrayDeque<E> {
  public E pop() {
    try {
      return super.pop();
    }
    catch( NoSuchElementException nsee ) {
      return create();
    }
  }

  public E create() {
    try {
      Type sooper = getClass().getGenericSuperclass();
      Type t = ((ParameterizedType)sooper).getActualTypeArguments()[ 0 ];

      return (E)(Class.forName( t.toString() ).newInstance());
    }
    catch( Exception e ) {
      return null;
    }
  }
}

그때:

public class Main {
    // Note the braces...
    private Deque<String> stack = new SilentStack<String>(){};

    public static void main( String args[] ) {
      // Returns a new instance of String.
      String s = stack.pop();
      System.out.printf( "s = '%s'\n", s );
    }
}

표준 접근 / 해결 방법 / 솔루션은 class다음과 같이 생성자에 객체를 추가하는 것입니다.

 public class Foo<T> {

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

    public Class<T> getType() {
      return type;
    }

    public T newInstance() {
      return type.newInstance();
    }
 }

일반적인 추상 수퍼 클래스가 있다고 상상해보십시오.

public abstract class Foo<? extends T> {}

그런 다음 T를 확장하는 일반 Bar로 Foo를 확장하는 두 번째 클래스가 있습니다.

public class Second extends Foo<Bar> {}

(bert bruynooghe 답변에서) Bar.class선택하고 인스턴스를 Type사용하여 추론 하여 Foo 클래스 의 클래스 가져올 수 있습니다 Class.

Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
//Parse it as String
String className = tType.toString().split(" ")[1];
Class clazz = Class.forName(className);

이 작업은 이상적이지 않으므로 여러 계산을 피하기 위해 계산 된 값을 캐시하는 것이 좋습니다. 일반적인 용도 중 하나는 일반 DAO 구현입니다.

최종 구현 :

public abstract class Foo<T> {

    private Class<T> inferedClass;

    public Class<T> getGenericClass(){
        if(inferedClass == null){
            Type mySuperclass = getClass().getGenericSuperclass();
            Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
            String className = tType.toString().split(" ")[1];
            inferedClass = Class.forName(className);
        }
        return inferedClass;
    }
}

반환 된 값은 다른 함수의 Foo 클래스 또는 Bar 클래스에서 호출 될 때 Bar.class입니다.


다음은 작동하는 솔루션입니다.

@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>) 로 확장해야합니다.

또는

  1. 익명 구현으로 생성되어야 함 ( new Generic<Integer>() {};)

유형 삭제 때문에 할 수 없습니다. 스택 오버플로 질문 Java 제네릭 유형 삭제시기와 상황을 참조하십시오 .


추상 제네릭 클래스 에서이 문제가 발생했습니다. 이 특별한 경우 솔루션은 더 간단합니다.

abstract class Foo<T> {
    abstract Class<T> getTClass();
    //...
}

나중에 파생 클래스에서 :

class Bar extends Foo<Whatever> {
    @Override
    Class<T> getTClass() {
        return Whatever.class;
    }
}

다른 사람들이 제안한 클래스보다 더 나은 경로는 클래스로했던 작업을 수행 할 수있는 객체를 전달하는 것입니다 (예 : 새 인스턴스 만들기).

interface Factory<T> {
  T apply();
}

<T> void List<T> make10(Factory<T> factory) {
  List<T> result = new ArrayList<T>();
  for (int a = 0; a < 10; a++)
    result.add(factory.apply());
  return result;
}

class FooFactory<T> implements Factory<Foo<T>> {
  public Foo<T> apply() {
    return new Foo<T>();
  }
}

List<Foo<Integer>> foos = make10(new FooFactory<Integer>());

최근에 사용한이 문제에 대한 (추악하지만 효과적인) 해결책이 있습니다.

import java.lang.reflect.TypeVariable;


public static <T> Class<T> getGenericClass()
{
    __<T> ins = new __<T>();
    TypeVariable<?>[] cls = ins.getClass().getTypeParameters(); 

    return (Class<T>)cls[0].getClass();
}

private final class __<T> // generic helper class which does only provide type information
{
    private __()
    {
    }
}

있을 수있다:

class Foo<T> {
  Class<T> clazz = (Class<T>) DAOUtil.getTypeArguments(Foo.class, this.getClass()).get(0);
}

svn / trunk / dao / src / main / java / com / googlecode / genericdao / dao / DAOUtil.java 에서 두 가지 함수가 필요합니다 .

자세한 설명은 제네릭 반영을 참조하십시오 .


나는 그것을 수행하는 일반적이고 간단한 방법을 찾았습니다. 내 클래스에서 클래스 정의의 위치에 따라 제네릭 유형을 반환하는 메서드를 만들었습니다. 다음과 같은 클래스 정의를 가정 해 보겠습니다.

public class MyClass<A, B, C> {

}

이제 유형을 유지하기 위해 몇 가지 속성을 만들어 보겠습니다.

public class MyClass<A, B, C> {

    private Class<A> aType;

    private Class<B> bType;

    private Class<C> cType;

// Getters and setters (not necessary if you are going to use them internally)

    } 

그런 다음 제네릭 정의의 인덱스를 기반으로 유형을 반환하는 제네릭 메서드를 만들 수 있습니다.

   /**
     * Returns a {@link Type} object to identify generic types
     * @return type
     */
    private Type getGenericClassType(int index) {
        // To make it use generics without supplying the class type
        Type type = getClass().getGenericSuperclass();

        while (!(type instanceof ParameterizedType)) {
            if (type instanceof ParameterizedType) {
                type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
            } else {
                type = ((Class<?>) type).getGenericSuperclass();
            }
        }

        return ((ParameterizedType) type).getActualTypeArguments()[index];
    }

마지막으로 생성자에서 메서드를 호출하고 각 유형에 대한 인덱스를 보냅니다. 완전한 코드는 다음과 같아야합니다.

public class MyClass<A, B, C> {

    private Class<A> aType;

    private Class<B> bType;

    private Class<C> cType;


    public MyClass() {
      this.aType = (Class<A>) getGenericClassType(0);
      this.bType = (Class<B>) getGenericClassType(1);
      this.cType = (Class<C>) getGenericClassType(2);
    }

   /**
     * Returns a {@link Type} object to identify generic types
     * @return type
     */
    private Type getGenericClassType(int index) {

        Type type = getClass().getGenericSuperclass();

        while (!(type instanceof ParameterizedType)) {
            if (type instanceof ParameterizedType) {
                type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
            } else {
                type = ((Class<?>) type).getGenericSuperclass();
            }
        }

        return ((ParameterizedType) type).getActualTypeArguments()[index];
    }
}

다른 답변에서 설명한 것처럼이 ParameterizedType접근 방식 을 사용 하려면 클래스를 확장해야하지만 확장하는 완전히 새로운 클래스를 만드는 추가 작업처럼 보입니다 ...

따라서 클래스를 추상화하면이를 확장해야하므로 서브 클래 싱 요구 사항을 충족합니다. (lombok의 @Getter 사용).

@Getter
public abstract class ConfigurationDefinition<T> {

    private Class<T> type;
    ...

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

이제 새 클래스를 정의하지 않고 확장합니다. (끝의 {}에주의하십시오 ... 확장되지만 원하는 경우가 아니면 아무것도 덮어 쓰지 마십시오).

private ConfigurationDefinition<String> myConfigA = new ConfigurationDefinition<String>(...){};
private ConfigurationDefinition<File> myConfigB = new ConfigurationDefinition<File>(...){};
...
Class stringType = myConfigA.getType();
Class fileType = myConfigB.getType();

   public <T> T yourMethodSignature(Class<T> type) {

        // get some object and check the type match the given type
        Object result = ...            

        if (type.isAssignableFrom(result.getClass())) {
            return (T)result;
        } else {
            // handle the error
        }
   }

generics를 사용하는 클래스 / 인터페이스를 확장하거나 구현하는 경우 기존 클래스 / 인터페이스를 전혀 수정하지 않고도 부모 클래스 / 인터페이스의 Generic Type을 얻을 수 있습니다.

세 가지 가능성이있을 수 있습니다.

사례 1 클래스가 Generics를 사용하는 클래스를 확장하는 경우

public class TestGenerics {
    public static void main(String[] args) {
        Type type = TestMySuperGenericType.class.getGenericSuperclass();
        Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
        for(Type gType : gTypes){
            System.out.println("Generic type:"+gType.toString());
        }
    }
}

class GenericClass<T> {
    public void print(T obj){};
}

class TestMySuperGenericType extends GenericClass<Integer> {
}

사례 2 클래스가 Generics를 사용하는 인터페이스를 구현할 때

public class TestGenerics {
    public static void main(String[] args) {
        Type[] interfaces = TestMySuperGenericType.class.getGenericInterfaces();
        for(Type type : interfaces){
            Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
            for(Type gType : gTypes){
                System.out.println("Generic type:"+gType.toString());
            }
        }
    }
}

interface GenericClass<T> {
    public void print(T obj);
}

class TestMySuperGenericType implements GenericClass<Integer> {
    public void print(Integer obj){}
}

사례 3 인터페이스가 Generics를 사용하는 인터페이스를 확장하는 경우

public class TestGenerics {
    public static void main(String[] args) {
        Type[] interfaces = TestMySuperGenericType.class.getGenericInterfaces();
        for(Type type : interfaces){
            Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
            for(Type gType : gTypes){
                System.out.println("Generic type:"+gType.toString());
            }
        }
    }
}

interface GenericClass<T> {
    public void print(T obj);
}

interface TestMySuperGenericType extends GenericClass<Integer> {
}

그것은 매우 간단합니다. 같은 클래스 내에서 필요한 경우 :

Class clazz = this.getClass();
ParameterizedType parameterizedType = (ParameterizedType) clazz.getGenericSuperclass();
try {
        Class typeClass = Class.forName( parameterizedType.getActualTypeArguments()[0].getTypeName() );
        // You have the instance of type 'T' in typeClass variable

        System.out.println( "Class instance name: "+  typeClass.getName() );
    } catch (ClassNotFoundException e) {
        System.out.println( "ClassNotFound!! Something wrong! "+ e.getMessage() );
    }

실제로, 클래스에 T 유형의 필드가 있다고 가정합니다. 유형 T의 필드가없는 경우 일반 유형을 갖는 이유는 무엇입니까? 따라서 해당 필드에서 간단히 instanceof를 수행 할 수 있습니다.

제 경우에는

목록 <T> 항목;
내 수업에서 클래스 유형이 "Locality"인지 확인합니다.

if (items.get (0) instanceof Locality) ...

물론 이것은 가능한 총 클래스 수가 제한된 경우에만 작동합니다.


이 질문은 오래되었지만 지금은 google을 사용하는 것이 가장 좋습니다 Gson.

사용자 지정 viewModel.

Class<CustomViewModel<String>> clazz = new GenericClass<CustomViewModel<String>>().getRawType();
CustomViewModel<String> viewModel = viewModelProvider.get(clazz);

일반 유형 클래스

class GenericClass<T>(private val rawType: Class<*>) {

    constructor():this(`$Gson$Types`.getRawType(object : TypeToken<T>() {}.getType()))

    fun getRawType(): Class<T> {
        return rawType as Class<T>
    }
}

이에 대한 해결 방법을 사용하고 있습니다.

class MyClass extends Foo<T> {
....
}

MyClass myClassInstance = MyClass.class.newInstance();

참고 URL : https://stackoverflow.com/questions/3437897/how-to-get-a-class-instance-of-generics-type-t

반응형