Java泛型 - 获取类?

106
我有一个列表,编程如下:public class MyList<T>。是否有办法使用T变量来获取类的名称(这样我就可以在MyList中知道T是String、Socket等)?
编辑:不用管了,在这里找到了答案:https://dev59.com/vnA75IYBdhLWcg3wKluM

https://en.wikipedia.org/wiki/Criticism_of_Java#Generics - granadaCoder
5个回答

141
**简短回答**: 你不能。
**详细回答**:
由于Java中泛型的实现方式,泛型类型T并没有在运行时保留。不过,你可以使用一个私有数据成员:
public class Foo<T> 
{
    private Class<T> type;

    public Foo(Class<T> type) { this.type = type; } 
}

使用示例:

Foo<Integer> test = new Foo<Integer>(Integer.class);

75

这个解决方案对我来说很好用。优点是不需要像其他解决方案中提到的那样传递额外的参数。在使用时有任何问题吗? - Yasitha Waduge
它仍然工作正常。 - wutzebaer
21
只有在运行时类是通用超类的直接子类时,此方法才有效。 - Rangi Keen
这会导致反射方面的显著性能损失吗? - Quantium
1
这在现代框架中广泛使用的动态代理中不起作用 - gavenkoa
4
主线程中出现异常:"main" java.lang.ClassCastException:java.lang.Class无法强制转换为java.lang.reflect.ParameterizedType。 - User

28
您正在看到的是类型擦除的结果。当一个泛型类型被实例化时,编译器通过一种称为类型擦除的技术来将这些类型转换,即在类或方法中删除与类型参数和类型参数相关的所有信息。类型擦除使得使用泛型的Java应用程序能够与在泛型出现之前创建的Java库和应用程序保持二进制兼容性。
例如,Box<String>被转换为类型Box,这被称为原始类型 - 原始类型是没有任何类型参数的泛型类或接口名称。这意味着您无法在运行时找到泛型类正在使用哪种类型的对象。
这也像这个问题一样,该问题有一个非常好的答案

4

我不确定这在所有情况下都有效(至少需要Java 1.5):

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

public class Main 
{
    public class A
    {   
    }

    public class B extends A
    {       
    }


    public Map<A, B> map = new HashMap<Main.A, Main.B>();

    public static void main(String[] args) 
    {

        try
        {
            Field field = Main.class.getField("map");           
            System.out.println("Field " + field.getName() + " is of type " + field.getType().getSimpleName());

            Type genericType = field.getGenericType();

            if(genericType instanceof ParameterizedType)
            {
                ParameterizedType type = (ParameterizedType) genericType;               
                Type[] typeArguments = type.getActualTypeArguments();

                for(Type typeArgument : typeArguments) 
                {   
                    Class<?> classType = ((Class<?>)typeArgument);                  
                    System.out.println("Field " + field.getName() + " has a parameterized type of " + classType.getSimpleName());
                }
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }    
}

这将输出:

字段映射的类型为Map
字段映射具有参数化类型A
字段映射具有参数化类型B


曾经,在重构General DAO期间,我尝试过类似的做法,于是我浏览了一些类似的代码示例。不记得为什么了,但我决定不采用这种方法(我认为在所有情况下都不起作用,也许类层次结构是必须的)。你的例子确实行得通:)+1 附注:我找到了一些扩展教程:http://www.artima.com/weblogs/viewpost.jsp?thread=208860 - Art Licis
我找到了这篇文章的原始来源(上面的示例是从我们的工作项目中提取的):http://tutorials.jenkov.com/java-reflection/generics.html#fieldtypes。该文章称这仅适用于公共字段,但事实并非如此,您也可以通过反射访问受保护和私有字段(只需使用getDeclaredField而不是getField)。 - esaj
2
这并没有回答问题。这只是获取用于声明字段的类型。这与问题有什么关系? - newacct

3

这里,MyList.class和this.getClass()的值是相同的! - User
可以的。这是有效的: public String getParameterTypeName() { return ((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]) .getTypeName(); } - Michael Zheng
https://www.codeproject.com/Questions/5335039/Get-generic-type-name-in-java - Michael Zheng

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接