제네릭 클래스가 Foo<T>
있습니다. 의 메소드에서 Foo
유형의 클래스 인스턴스를 가져오고 T
싶지만 호출 할 수는 없습니다 T.class
.
를 사용하여 해결하는 가장 좋은 방법은 무엇입니까 T.class
?
import com.fasterxml.jackson.core.type.TypeReference;
new TypeReference<T>(){}
제네릭 클래스가 Foo<T>
있습니다. 의 메소드에서 Foo
유형의 클래스 인스턴스를 가져오고 T
싶지만 호출 할 수는 없습니다 T.class
.
를 사용하여 해결하는 가장 좋은 방법은 무엇입니까 T.class
?
import com.fasterxml.jackson.core.type.TypeReference;
new TypeReference<T>(){}
답변:
짧은 대답은 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
}
}
typeParameterClass
생성자에 기본 할당없이 할당하는 것은 완벽합니다. 두 번째로 설정할 필요가 없습니다.
클래스 경로에 추가 종속성을 추가하지 않고 직접 수행 할 수있는 방법을 찾고있었습니다. 일부 조사 후 나는 것을 발견 하다 만큼 당신이 일반 슈퍼를 가지고 가능. 일반 레이어 수퍼 타입 이있는 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 );
}
}
TypeLiteral
a
와 b
모두 같은 클래스를 확장하지만 동일한 인스턴스 수업을합니다 생성이 방법. a.getClass() != b.getClass()
표준 접근법 / 해결 방법 / 솔루션은 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입니다.
toString().split(" ")[1]
문제는 피하십시오"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 제네릭 형식 삭제시기 및 발생시 기도 참조하십시오 .
다른 사람들이 제안한 클래스보다 더 나은 경로는 클래스로 수행 한 작업을 수행 할 수있는 객체를 전달하는 것입니다 (예 : 새 인스턴스 만들기).
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>());
나는 추상 제네릭 클래스 에서이 문제가있었습니다. 이 특별한 경우 솔루션이 더 간단합니다.
abstract class Foo<T> {
abstract Class<T> getTClass();
//...
}
나중에 파생 클래스에서 :
class Bar extends Foo<Whatever> {
@Override
Class<T> getTClass() {
return Whatever.class;
}
}
나는 최근에 사용한이 문제에 대한 (못하지만 효과적인) 해결책을 가지고 있습니다.
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);
}
hibernate-generic-dao / blob / master / 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();
generics를 사용하는 클래스 / 인터페이스를 확장하거나 구현하는 경우 기존 클래스 / 인터페이스를 전혀 수정하지 않고 부모 클래스 / 인터페이스의 일반 유형을 얻을 수 있습니다.
세 가지 가능성이있을 수 있습니다.
사례 1 클래스가 제네릭을 사용하는 클래스를 확장하는 경우
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를 수행 할 수 있습니다.
내 경우에는
List <T> 항목;내 수업에서 클래스 유형이 "지역"인지 확인합니다.
if (items.get (0) instanceof Locality) ...
물론 이것은 가능한 총 수업 수가 제한되어있는 경우에만 작동합니다.
이 질문은 오래되었지만 이제는 google 사용하는 것이 가장 좋습니다 Gson
.
custom을 얻는 예제 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>
}
}
Generics를 사용하는 메소드에 T.class를 전달하고 싶었습니다.
readFile 메소드는 fullPath 를 사용하여 fileName으로 지정된 .csv 파일을 읽습니다. 내용이 다른 csv 파일이있을 수 있으므로 적절한 객체를 얻을 수 있도록 모델 파일 클래스를 전달해야합니다. 이것은 CSV 파일을 읽고 있기 때문에 일반적인 방법으로하고 싶었습니다. 어떤 이유로 든 위의 해결책 중 어느 것도 나를 위해 일하지 않았습니다. Class<? extends T> type
작동 하려면 사용해야
합니다. CSV 파일을 구문 분석하기 위해 opencsv 라이브러리를 사용합니다.
private <T>List<T> readFile(String fileName, Class<? extends T> type) {
List<T> dataList = new ArrayList<T>();
try {
File file = new File(fileName);
Reader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
Reader headerReader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
CSVReader csvReader = new CSVReader(headerReader);
// create csv bean reader
CsvToBean<T> csvToBean = new CsvToBeanBuilder(reader)
.withType(type)
.withIgnoreLeadingWhiteSpace(true)
.build();
dataList = csvToBean.parse();
}
catch (Exception ex) {
logger.error("Error: ", ex);
}
return dataList;
}
이것이 readFile 메소드가 호출되는 방식입니다
List<RigSurfaceCSV> rigSurfaceCSVDataList = readSurfaceFile(surfaceFileName, RigSurfaceCSV.class);
이 해결 방법을 사용하고 있습니다.
class MyClass extends Foo<T> {
....
}
MyClass myClassInstance = MyClass.class.newInstance();