제네릭 유형 T의 클래스 인스턴스를 얻는 방법
제네릭 클래스가 Foo<T>
있습니다. 의 메서드에서 Foo
T 유형의 클래스 인스턴스를 가져오고 싶지만을 호출 할 수 없습니다 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 <> ");
}
}
참고 : 슈퍼 클래스로만 사용할 수 있습니다 .
- 유형이 지정된 클래스 (
Child extends Generic<Integer>
) 로 확장해야합니다.
또는
- 익명 구현으로 생성되어야 함 (
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
'programing tip' 카테고리의 다른 글
Xcode 6 : 시뮬레이터에 키보드가 표시되지 않음 (0) | 2020.10.02 |
---|---|
배치 파일 / cmd에서 5 초 동안 잠자는 방법 (0) | 2020.10.02 |
함수 이름을 문자열로 얻는 방법? (0) | 2020.10.02 |
문자열이 JS의 정규식과 일치하는지 확인 (0) | 2020.10.02 |
어떤 Python 메모리 프로파일 러가 권장됩니까? (0) | 2020.10.02 |