[java] 제네릭 형식 T의 클래스 인스턴스를 어떻게 얻습니까?

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

를 사용하여 해결하는 가장 좋은 방법은 무엇입니까 T.class?



답변

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

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

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를 사용하여이를 수행하는 방법을 설명합니다.

@SuppressWarnings("unchecked")
public GenericJpaDao() {
  this.entityBeanType = ((Class) ((ParameterizedType) getClass()
      .getGenericSuperclass()).getActualTypeArguments()[0]);
}

스프링 솔루션

내 프로젝트는 Spring 을 Spring에는 유형을 찾기위한 편리한 유틸리티 방법이 있으므로 더 좋습니다. 가장보기 흉한 것처럼 보이기 때문에 이것이 최선의 방법입니다. 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를 확장하는 일반 막대로 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 제네릭 형식 삭제시기 및 발생시 기도 참조하십시오 .