我怎样才能做到这一点?
public class GenericClass<T>
{
public Type getMyType()
{
//How do I return the type of T?
}
}
到目前为止,我尝试过的所有操作都返回类型 Object
而不是使用的特定类型。
正如其他人所提到的,只有在某些情况下通过反射才有可能。
如果您真的需要该类型,这是通常的(类型安全)解决方法模式:
public class GenericClass<T> {
private final Class<T> type;
public GenericClass(Class<T> type) {
this.type = type;
}
public Class<T> getMyType() {
return this.type;
}
}
我见过这样的事情
private Class<T> persistentClass;
public Constructor() {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}
在 hibernate GenericDataAccessObjects 示例中
class A implements Comparable<String>
中,实际类型参数是 String
,但它 CANNOT 告诉在 Set<String> a = new TreeSet<String>()
中,实际类型参数是 String
。事实上,类型参数信息在编译后被“擦除”,如其他答案中所述。
java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
这个答案。
Class-Mate
来实现。我在这里写了一个要点gist.github.com/yunspace/930d4d40a787a1f6a7d1
(Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()
才能获得实际的类型参数。
泛型在运行时没有具体化。这意味着信息在运行时不存在。
在保持向后兼容性的同时向 Java 添加泛型是一项艰巨的任务(您可以查看有关它的开创性论文:Making the future safe for the past: adding genericity to the Java programming language)。
关于这个主题有丰富的文献,有些人对当前状态dissatisfied,有些人说实际上它是一个lure并且没有真正需要它。你可以阅读这两个链接,我发现它们很有趣。
使用番石榴。
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
}
}
不久前,我发布了一些完整的示例,包括抽象类和子类 here。
注意:这要求您实例化 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
GenericClass
的子类,因此您应该将该类设为 abstract
,这样错误的用法就不会编译。
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[]
你当然可以。
出于向后兼容性的原因,Java 在运行时不使用这些信息。但是这些信息实际上是作为元数据存在的,可以通过反射访问(但它仍然不用于类型检查)。
来自官方 API:
但是,对于您的情况,我不会使用反射。我个人更倾向于将其用于框架代码。在您的情况下,我只需将类型添加为构造函数参数。
actualTypeArguments
,但我不知道如何实际访问它。
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 文档:
将类型擦除泛型引入 Java 语言以在编译时提供更严格的类型检查并支持泛型编程。为了实现泛型,Java 编译器将类型擦除应用于: 将泛型类型中的所有类型参数替换为其边界或 Object(如果类型参数是无界的)。因此,生成的字节码只包含普通的类、接口和方法。必要时插入类型转换以保持类型安全。生成桥方法以保留扩展泛型类型中的多态性。类型擦除确保不会为参数化类型创建新类;因此,泛型不会产生运行时开销。
http://docs.oracle.com/javase/tutorial/java/generics/erasure.html
此 article by 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();
我认为还有另一个优雅的解决方案。
您想要做的是(安全地)将泛型类型参数的类型从具体类“传递”到超类。
如果您允许自己将类类型视为类上的“元数据”,则建议使用 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();
}
这种方法的一些限制是:
您在两个地方而不是一个非 DRY 的地方指定泛型类型 (PassedGenericType)。这只有在您可以修改具体子类时才有可能。
这是一种方法,我不得不使用一次或两次:
public abstract class GenericClass<T>{
public abstract Class<T> getMyType();
}
随着
public class SpecificClass extends GenericClass<String>{
@Override
public Class<String> getMyType(){
return String.class;
}
}
这是我的解决方案:
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>
)
OR
2 进行扩展。必须创建为匿名实现 (new Generic<Integer>() {};
)
你不能。如果您将 T 类型的成员变量添加到类中(您甚至不必初始化它),您可以使用它来恢复类型。
该驾驶室的一个简单解决方案如下
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 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)
这是我的窍门:
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();
}
}
万一您使用泛型类型存储变量,您可以通过添加 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();
在应该返回 Number.class
时返回 Integer.class
。返回 Class<? extends T>
会更正确。 Integer.class
是一个 Class<? extends Number>
但不是 Class<Number>
。
这是我的解决方案。这些例子应该解释它。唯一的要求是子类必须设置泛型类型,而不是对象。
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;
}
}
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}
如果你有这样的课程:
public class GenericClass<T> {
private T data;
}
使用 T
变量,然后您可以打印 T
名称:
System.out.println(data.getClass().getSimpleName()); // "String", "Integer", etc.
data == null
。在这种情况下,我们无法获得类型。
如果您正在使用弹簧:
public static Class<?>[] resolveTypeArguments(Class<?> parentClass, Class<?> subClass) {
if (subClass.isSynthetic()) {
return null;
}
return GenericTypeResolver.resolveTypeArguments(subClass, parentClass);
}
顺便说一句,对于非子类类,GenericTypeResolver
仍然会像提到的问题一样为 null,因为此类类的通用信息在编译后完全是 erased。
解决这个问题的唯一方法可能是:
public class GenericClass<T>
{
private final Class<T> clazz;
public Foo(Class<T> clazz) {
this.clazz= clazz;
}
public Type getMyType()
{
return clazz;
}
}
如果您无法更改泛型类并使用本页已说明的方法之一,那么简单的方法是根据运行时实例类名称获取类型类。
Class getType(GenericType runtimeClassMember){
if (ClassA.class.equals(runtimeClassMember.getClass()){
return TypeForClassA.class;
} else if (ClassB.class.equals(runtimeClassMember.getClass()){
return TypeForClassB.class;
}
//throw an expectation or do whatever you want for the cases not described in the if section.
}
使用返回类类型的抽象方法,然后在该类中使用它,并且无论您在何处扩展泛型类,都必须实现该抽象方法以返回所需的类类型
public class AbsractService<T>{
public abstract Class<T> getClassType ();
.......
}
在运行时
class AnimalService extends AbstractService<MyType>{
public Class<MyType> getClassType (){
return MyType.class;
}
.....
}
我和上面的@Moesio 做了同样的事情,但在 Kotlin 中可以这样做:
class A<T : SomeClass>() {
var someClassType : T
init(){
this.someClassType = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<T>
}
}
这是受到 Pablo 和 CoolMind 的回答的启发。有时我也使用了 kayz1 的答案中的技术(也表达在许多其他答案中),我相信这是一种体面且可靠的方式来完成 OP 的要求。
我首先选择将其定义为接口(类似于 PJWeisberg),因为我有可以从该功能中受益的现有类型,特别是异构通用联合类型:
public interface IGenericType<T>
{
Class<T> getGenericTypeParameterType();
}
我在通用匿名接口实现中的简单实现如下所示:
//Passed into the generic value generator function: toStore
//This value name is a field in the enclosing class.
//IUnionTypeValue<T> is a generic interface that extends IGenericType<T>
value = new IUnionTypeValue<T>() {
...
private T storedValue = toStore;
...
@SuppressWarnings("unchecked")
@Override
public Class<T> getGenericTypeParameterType()
{
return (Class<T>) storedValue.getClass();
}
}
我想这也可以通过使用类定义对象作为源来构建,这只是一个单独的用例。我认为关键是正如许多其他答案所说,以一种或另一种方式,您需要在运行时获取类型信息才能在运行时使用它;对象本身保持它们的类型,但是擦除(也正如其他人所说,使用适当的引用)会导致任何封闭/容器类型丢失该类型信息。
它可能对某人有用。您可以使用 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)
。 (这就是为什么这段代码不起作用。)
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)
。好看一点。