제네릭 클래스가 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 <> ");
}
}
참고 :
수퍼 클래스로만 사용할 수 있습니다
- 형식화 된 클래스 (
Child extends Generic<Integer>
) 로 확장해야합니다.
또는
- 익명 구현으로 작성해야합니다 (
new Generic<Integer>() {};
)