답변:
다른 사람들이 언급했듯이 특정 상황에서는 반사를 통해서만 가능합니다.
실제로 유형이 필요한 경우 일반적인 (유형 안전) 해결 방법 패턴입니다.
public class GenericClass<T> {
private final Class<T> type;
public GenericClass(Class<T> type) {
this.type = type;
}
public Class<T> getMyType() {
return this.type;
}
}
Foo foo1 = GetDao<Foo>(Foo.class).get(Foo.class, 1)
public static <T> GenericClass<T> of(Class<T> type) {...}
다음과 같은 호출 : GenericClass<String> var = GenericClass.of(String.class)
. 조금 더 멋지다.
나는 이런 것을 보았다
private Class<T> persistentClass;
public Constructor() {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}
class A implements Comparable<String>
실제 유형 매개 변수가 String
있지만 수없는 에서 말할 Set<String> a = new TreeSet<String>()
실제 유형 매개 변수입니다 String
. 실제로 다른 매개 변수에서 설명한 것처럼 형식 매개 변수 정보는 컴파일 후 "삭제"됩니다.
java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
이 답변을 받고 있습니다.
Class-Mate
Jackson 사람들로부터 도 얻을 수 있습니다 . 나는 여기에 요점을 썼다 gist.github.com/yunspace/930d4d40a787a1f6a7d1
(Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()
실제 유형 인수를 얻기 위해 전화 해야했습니다.
제네릭이되지 않습니다 구체화 실행시. 이는 정보가 런타임에 존재하지 않음을 의미합니다.
투어 드 힘이 자바에 제네릭을 이전 버전과의 호환성을 mantaining하는 동안 추가 (당신은 그것에 대해 독창적 인 논문을 볼 수 있습니다 : 과거에 대한 미래의 안전을 만들기 : 자바 프로그래밍 언어들이 generic을 추가 ).
이 주제에 대한 풍부한 문헌이 있으며 일부 사람들은 현재 상태에 불만을 가지고 있으며 , 일부 사람들은 실제로 그것이 미끼 이며 실제로 필요하지 않다고 말합니다. 두 링크를 모두 읽을 수 있습니다.
구아바를 사용하십시오.
import com.google.common.reflect.TypeToken;
import java.lang.reflect.Type;
public abstract class GenericClass<T> {
private final TypeToken<T> typeToken = new TypeToken<T>(getClass()) { };
private final Type type = typeToken.getType(); // or getRawType() to return Class<? super T>
public Type getType() {
return type;
}
public static void main(String[] args) {
GenericClass<String> example = new GenericClass<String>() { };
System.out.println(example.getType()); // => class java.lang.String
}
}
얼마 전 추상 클래스와 하위 클래스를 포함한 본격적인 예제를 여기에 게시했습니다 .
참고 :이 당신이 인스턴스화해야합니다 서브 클래스 의 GenericClass
제대로 유형 매개 변수를 바인딩 할 수 있도록합니다. 그렇지 않으면 유형을로 반환합니다 T
.
java.lang.IllegalArgumentException: class com.google.common.reflect.TypeToken isn't parameterized
. 그래서 줄 new TypeToken(getClass()) { }
을으로 변경 했습니다 new TypeToken<T>(getClass()) { }
. 이제 코드가 제대로 실행되지만 Type은 여전히 'T'입니다. 참조 : gist.github.com/m-manu/9cda9d8f9d53bead2035
default Type getParameterType() { final TypeToken<T> typeToken = new TypeToken<T>(getClass()) {}; final Type type = typeToken.getType(); return type; }
GenericClass
해당 클래스를 만들어야합니다 abstract
.
물론 넌 할 수있어.
Java는 이전 버전과의 호환성을 위해 런타임에 정보를 사용 하지 않습니다 . 그러나 정보는 실제로 메타 데이터로 존재 하며 리플렉션을 통해 액세스 할 수 있습니다 (그러나 여전히 유형 확인에는 사용되지 않습니다).
공식 API에서 :
그러나 귀하의 시나리오에서는 리플렉션을 사용하지 않습니다. 개인적으로 프레임 워크 코드에 사용하는 경향이 있습니다. 귀하의 경우 유형을 생성자 매개 변수로 추가합니다.
Java 제네릭은 대부분 컴파일 시간이므로 유형 정보가 런타임에 손실됩니다.
class GenericCls<T>
{
T t;
}
같은 것으로 컴파일됩니다
class GenericCls
{
Object o;
}
런타임에 유형 정보를 얻으려면 ctor의 인수로 추가해야합니다.
class GenericCls<T>
{
private Class<T> type;
public GenericCls(Class<T> cls)
{
type= cls;
}
Class<T> getType(){return type;}
}
예:
GenericCls<?> instance = new GenericCls<String>(String.class);
assert instance.getType() == String.class;
private final Class<T> type;
Type t = //String[]
public abstract class AbstractDao<T>
{
private final Class<T> persistentClass;
public AbstractDao()
{
this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
}
나는 다음과 같은 접근 방식을 사용했습니다.
public class A<T> {
protected Class<T> clazz;
public A() {
this.clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
public Class<T> getClazz() {
return clazz;
}
}
public class B extends A<C> {
/* ... */
public void anything() {
// here I may use getClazz();
}
}
Java가 컴파일 할 때 유형 삭제를 사용하므로 코드가 프리 제너릭으로 작성된 응용 프로그램 및 라이브러리와 호환됩니다.
Oracle Docs에서 :
타입 소거
컴파일시에 더 엄격한 유형 검사를 제공하고 일반 프로그래밍을 지원하기 위해 Java 언어에 제네릭이 도입되었습니다. 제네릭을 구현하기 위해 Java 컴파일러는 유형 삭제를 다음에 적용합니다.
제네릭 형식의 모든 형식 매개 변수를 해당 경계 또는 형식 매개 변수가 제한되지 않은 경우 Object로 바꿉니다. 따라서 생성 된 바이트 코드에는 일반 클래스, 인터페이스 및 메소드 만 포함됩니다. 유형 안전을 유지하기 위해 필요한 경우 유형 캐스트를 삽입하십시오. 확장 된 제네릭 형식에서 다형성을 유지하기 위해 브리지 방법을 생성합니다. 유형 삭제는 매개 변수화 된 유형에 대해 새 클래스가 작성되지 않도록합니다. 결과적으로 제네릭은 런타임 오버 헤드를 발생시키지 않습니다.
http://docs.oracle.com/javase/tutorial/java/generics/erasure.html
이 기사에서 Ian Robertson 이 설명한 기술이 저에게 효과적입니다.
짧고 더러운 예를 들면 다음과 같습니다.
public abstract class AbstractDAO<T extends EntityInterface, U extends QueryCriteria, V>
{
/**
* Method returns class implementing EntityInterface which was used in class
* extending AbstractDAO
*
* @return Class<T extends EntityInterface>
*/
public Class<T> returnedClass()
{
return (Class<T>) getTypeArguments(AbstractDAO.class, getClass()).get(0);
}
/**
* Get the underlying class for a type, or null if the type is a variable
* type.
*
* @param type the type
* @return the underlying class
*/
public static Class<?> getClass(Type type)
{
if (type instanceof Class) {
return (Class) type;
} else if (type instanceof ParameterizedType) {
return getClass(((ParameterizedType) type).getRawType());
} else if (type instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) type).getGenericComponentType();
Class<?> componentClass = getClass(componentType);
if (componentClass != null) {
return Array.newInstance(componentClass, 0).getClass();
} else {
return null;
}
} else {
return null;
}
}
/**
* Get the actual type arguments a child class has used to extend a generic
* base class.
*
* @param baseClass the base class
* @param childClass the child class
* @return a list of the raw classes for the actual type arguments.
*/
public static <T> List<Class<?>> getTypeArguments(
Class<T> baseClass, Class<? extends T> childClass)
{
Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
Type type = childClass;
// start walking up the inheritance hierarchy until we hit baseClass
while (!getClass(type).equals(baseClass)) {
if (type instanceof Class) {
// there is no useful information for us in raw types, so just keep going.
type = ((Class) type).getGenericSuperclass();
} else {
ParameterizedType parameterizedType = (ParameterizedType) type;
Class<?> rawType = (Class) parameterizedType.getRawType();
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
for (int i = 0; i < actualTypeArguments.length; i++) {
resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
}
if (!rawType.equals(baseClass)) {
type = rawType.getGenericSuperclass();
}
}
}
// finally, for each actual type argument provided to baseClass, determine (if possible)
// the raw class for that type argument.
Type[] actualTypeArguments;
if (type instanceof Class) {
actualTypeArguments = ((Class) type).getTypeParameters();
} else {
actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
}
List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
// resolve types by chasing down type variables.
for (Type baseType : actualTypeArguments) {
while (resolvedTypes.containsKey(baseType)) {
baseType = resolvedTypes.get(baseType);
}
typeArgumentsAsClasses.add(getClass(baseType));
}
return typeArgumentsAsClasses;
}
}
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
또 다른 우아한 해결책이 있다고 생각합니다.
당신이하고 싶은 것은 일반 클래스 매개 변수의 타입을 concerete 클래스에서 수퍼 클래스로 (안전하게) 전달하는 것입니다.
클래스에서 클래스 유형을 "메타 데이터"로 생각할 수 있으면 런타임시 메타 데이터를 인코딩하는 Java 메소드 인 주석이 제안됩니다.
먼저 다음 행을 따라 사용자 정의 주석을 정의하십시오.
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
Class entityClass();
}
그런 다음 서브 클래스에 주석을 추가해야합니다.
@EntityAnnotation(entityClass = PassedGenericType.class)
public class Subclass<PassedGenericType> {...}
그런 다음이 코드를 사용하여 기본 클래스에서 클래스 유형을 가져올 수 있습니다.
import org.springframework.core.annotation.AnnotationUtils;
.
.
.
private Class getGenericParameterType() {
final Class aClass = this.getClass();
EntityAnnotation ne =
AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class);
return ne.entityClass();
}
이 접근법의 일부 제한 사항은 다음과 같습니다.
PassedGenericType
DRY 이외의 장소가 아닌 두 곳에서 일반 유형 ( )을 지정합니다 .이것은 내 솔루션입니다.
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
public class GenericClass<T extends String> {
public static void main(String[] args) {
for (TypeVariable typeParam : GenericClass.class.getTypeParameters()) {
System.out.println(typeParam.getName());
for (Type bound : typeParam.getBounds()) {
System.out.println(bound);
}
}
}
}
여기에 해결책이 있습니다 !!!
@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>
)
로 확장해야합니다.
또는
2. 익명 구현 ( new Generic<Integer>() {};
) 으로 만들어야합니다.
한두 가지 방법으로 한두 번 사용해야했습니다.
public abstract class GenericClass<T>{
public abstract Class<T> getMyType();
}
와 함께
public class SpecificClass extends GenericClass<String>{
@Override
public Class<String> getMyType(){
return String.class;
}
}
이 택시에 대한 간단한 해결책은 다음과 같습니다.
public class GenericDemo<T>{
private T type;
GenericDemo(T t)
{
this.type = t;
}
public String getType()
{
return this.type.getClass().getName();
}
public static void main(String[] args)
{
GenericDemo<Integer> obj = new GenericDemo<Integer>(5);
System.out.println("Type: "+ obj.getType());
}
}
여기에 대한 답변 중 일부를 완료하려면 재귀의 도움으로 계층 구조의 높이에 관계없이 MyGenericClass의 ParametrizedType을 가져와야했습니다.
private Class<T> getGenericTypeClass() {
return (Class<T>) (getParametrizedType(getClass())).getActualTypeArguments()[0];
}
private static ParameterizedType getParametrizedType(Class clazz){
if(clazz.getSuperclass().equals(MyGenericClass.class)){ // check that we are at the top of the hierarchy
return (ParameterizedType) clazz.getGenericSuperclass();
} else {
return getParametrizedType(clazz.getSuperclass());
}
}
내 요령은 다음과 같습니다.
public class Main {
public static void main(String[] args) throws Exception {
System.out.println(Main.<String> getClazz());
}
static <T> Class getClazz(T... param) {
return param.getClass().getComponentType();
}
}
T
. 경우 T
타입 변수의 가변 인자는 소거의 어레이를 생성한다 T
. 예를 들어 http://ideone.com/DIPNwd를 참조하십시오 .
제네릭 형식을 사용하여 변수를 저장하는 경우 getClassType 메서드를 추가하여 다음과 같이이 문제를 쉽게 해결할 수 있습니다.
public class Constant<T> {
private T value;
@SuppressWarnings("unchecked")
public Class<T> getClassType () {
return ((Class<T>) value.getClass());
}
}
제공된 클래스 객체를 나중에 다음과 같이 주어진 클래스의 인스턴스인지 확인합니다.
Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
Constant<Integer> integerConstant = (Constant<Integer>)constant;
Integer value = integerConstant.getValue();
// ...
}
value
이다 null
? 둘째 value
, 하위 클래스가 T
무엇입니까? Constant<Number> c = new Constant<Number>(new Integer(0)); Class<Number> n = c.getClassType();
반환 Integer.class
해야 할 때 반환 Number.class
합니다. 반환하는 것이 더 정확합니다 Class<? extends T>
. Integer.class
A는 Class<? extends Number>
아니지만 Class<Number>
.
여기 내 해결책이 있습니다
public class GenericClass<T>
{
private Class<T> realType;
public GenericClass() {
findTypeArguments(getClass());
}
private void findTypeArguments(Type t) {
if (t instanceof ParameterizedType) {
Type[] typeArgs = ((ParameterizedType) t).getActualTypeArguments();
realType = (Class<T>) typeArgs[0];
} else {
Class c = (Class) t;
findTypeArguments(c.getGenericSuperclass());
}
}
public Type getMyType()
{
// How do I return the type of T? (your question)
return realType;
}
}
클래스 계층 구조의 수준에 관계없이이 솔루션은 다음과 같이 작동합니다.
public class FirstLevelChild<T> extends GenericClass<T> {
}
public class SecondLevelChild extends FirstLevelChild<String> {
}
이 경우 getMyType () = java.lang.String
Exception in thread "main" java.lang.NullPointerException at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.<init>(Main.java:43) at Main.main(Main.java:61)
여기 내 해결책이 있습니다. 예제에서 설명해야합니다. 유일한 요구 사항은 서브 클래스가 오브젝트가 아닌 일반 유형을 설정해야한다는 것입니다.
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;
public class TypeUtils {
/*** EXAMPLES ***/
public static class Class1<A, B, C> {
public A someA;
public B someB;
public C someC;
public Class<?> getAType() {
return getTypeParameterType(this.getClass(), Class1.class, 0);
}
public Class<?> getCType() {
return getTypeParameterType(this.getClass(), Class1.class, 2);
}
}
public static class Class2<D, A, B, E, C> extends Class1<A, B, C> {
public B someB;
public D someD;
public E someE;
}
public static class Class3<E, C> extends Class2<String, Integer, Double, E, C> {
public E someE;
}
public static class Class4 extends Class3<Boolean, Long> {
}
public static void test() throws NoSuchFieldException {
Class4 class4 = new Class4();
Class<?> typeA = class4.getAType(); // typeA = Integer
Class<?> typeC = class4.getCType(); // typeC = Long
Field fieldSomeA = class4.getClass().getField("someA");
Class<?> typeSomeA = TypeUtils.getFieldType(class4.getClass(), fieldSomeA); // typeSomeA = Integer
Field fieldSomeE = class4.getClass().getField("someE");
Class<?> typeSomeE = TypeUtils.getFieldType(class4.getClass(), fieldSomeE); // typeSomeE = Boolean
}
/*** UTILS ***/
public static Class<?> getTypeVariableType(Class<?> subClass, TypeVariable<?> typeVariable) {
Map<TypeVariable<?>, Type> subMap = new HashMap<>();
Class<?> superClass;
while ((superClass = subClass.getSuperclass()) != null) {
Map<TypeVariable<?>, Type> superMap = new HashMap<>();
Type superGeneric = subClass.getGenericSuperclass();
if (superGeneric instanceof ParameterizedType) {
TypeVariable<?>[] typeParams = superClass.getTypeParameters();
Type[] actualTypeArgs = ((ParameterizedType) superGeneric).getActualTypeArguments();
for (int i = 0; i < typeParams.length; i++) {
Type actualType = actualTypeArgs[i];
if (actualType instanceof TypeVariable) {
actualType = subMap.get(actualType);
}
if (typeVariable == typeParams[i]) return (Class<?>) actualType;
superMap.put(typeParams[i], actualType);
}
}
subClass = superClass;
subMap = superMap;
}
return null;
}
public static Class<?> getTypeParameterType(Class<?> subClass, Class<?> superClass, int typeParameterIndex) {
return TypeUtils.getTypeVariableType(subClass, superClass.getTypeParameters()[typeParameterIndex]);
}
public static Class<?> getFieldType(Class<?> clazz, AccessibleObject element) {
Class<?> type = null;
Type genericType = null;
if (element instanceof Field) {
type = ((Field) element).getType();
genericType = ((Field) element).getGenericType();
} else if (element instanceof Method) {
type = ((Method) element).getReturnType();
genericType = ((Method) element).getGenericReturnType();
}
if (genericType instanceof TypeVariable) {
Class<?> typeVariableType = TypeUtils.getTypeVariableType(clazz, (TypeVariable) genericType);
if (typeVariableType != null) {
type = typeVariableType;
}
}
return type;
}
}
누군가에게 유용 할 수 있습니다. java.lang.ref.WeakReference를 사용할 수 있습니다. 이 방법:
class SomeClass<N>{
WeakReference<N> variableToGetTypeFrom;
N getType(){
return variableToGetTypeFrom.get();
}
}
WeakReference
? 코드가 아니라 답변에 대한 설명을 입력하십시오.
SomeClass<MyClass>
인스턴스 가 있으면 인스턴스를 인스턴스화 SomeClass
하고 호출 할 수 getType
있으며 런타임은입니다 MyClass
.
WeakReference
? 당신이 말한 것은 대부분의 다른 답변과 다르지 않습니다.
AtomicReference
, List
, Set
).
나는 이것이 간단하고 이해하기 쉬운 해결책이라는 것을 알았습니다.
public class GenericClass<T> {
private Class classForT(T...t) {
return t.getClass().getComponentType();
}
public static void main(String[] args) {
GenericClass<String> g = new GenericClass<String>();
System.out.println(g.classForT());
System.out.println(String.class);
}
}
(T...t)
. (그래서이 코드가 작동하지 않는 것입니다.)