Java:如何从泛型类型中获取类文字?

Tom*_*Tom 185 java generics class literals

通常情况下,我看到人们使用这样的类文字:

Class<Foo> cls = Foo.class;
Run Code Online (Sandbox Code Playgroud)

但是,如果类型是通用的,例如List?这工作正常,但有一个警告,因为List应该参数化:

Class<List> cls = List.class
Run Code Online (Sandbox Code Playgroud)

那么为什么不加一个<?>呢?好吧,这会导致类型不匹配错误:

Class<List<?>> cls = List.class
Run Code Online (Sandbox Code Playgroud)

我认为这样的东西可行,但这只是一个简单的'语法错误:

Class<List<Foo>> cls = List<Foo>.class
Run Code Online (Sandbox Code Playgroud)

我如何Class<List<Foo>>静态获取,例如使用类文字?

可以@SuppressWarnings("unchecked")摆脱造成在第一个例子中的非参数使用目录的警告,Class<List> cls = List.class但我宁愿不要.

有什么建议?

cle*_*tus 154

你不能因为类型擦除.

Java泛型仅仅是Object强制转换的语法糖.展示:

List<Integer> list1 = new ArrayList<Integer>();
List<String> list2 = (List<String>)list1;
list2.add("foo"); // perfectly legal
Run Code Online (Sandbox Code Playgroud)

在运行时保留泛型类型信息的唯一实例是Field.getGenericType()if通过反射询问类的成员.

所有这一切都是为什么Object.getClass()有这个签名:

public final native Class<?> getClass();
Run Code Online (Sandbox Code Playgroud)

重要的是Class<?>.

换句话说,来自Java Generics FAQ:

为什么没有具体参数化类型的类文字?

因为参数化类型没有确切的运行时类型表示.

类文字表示Class 表示给定类型的对象.例如,类literal String.class表示Class 表示类型的对象, String并且与在 Class对象上getClass调用方法时返回的 String对象相同.类文字可用于运行时类型检查和反射.

参数化类型在编译期间在称为类型擦除的过程中转换为字节代码时会丢失其类型参数.作为类型擦除的副作用,泛型类型的所有实例化共享相同的运行时表示,即相应原始类型的表示.换句话说,参数化类型没有自己的类型表示.因此,存在在形成文字类诸如没有意义的List<String>.class, List<Long>.class并且List<?>.class ,由于没有这样的Class对象是否存在.只有raw类型List有一个Class 表示其运行时类型的对象.它被称为 List.class.

  • 还有一个"它只适用于C#,但不适用于Java".我正在反序列化一个JSON对象,而typeof(List <MyClass>)在C#中运行得很好,但List <MyClass> .class是Java中的语法错误.是的,就像Cletus写的那样,有一个合乎逻辑的解释,但我总是想知道为什么所有这些东西都适用于C#. (13认同)
  • `List <Integer> list1 = new ArrayList <Integer>(); List <String> list2 =(List <String>)list1; list2.add( "富"); //完全合法`你可以在Java中做到这一点,你得到类型不匹配编译错误! (12认同)
  • 所以......如果需要,我该怎么办? (4认同)
  • 你总是可以通过`List <String> list2 =(List <String>)(Object)list1;`来欺骗编译器 (2认同)
  • 您什么意思完全合法?那部分代码无法编译? (2认同)

小智 58

参数化类型没有类文字,但是有正确定义这些类型的Type对象.

请参阅java.lang.reflect.ParameterizedType - http://java.sun.com/j2se/1.5.0/docs/api/java/lang/reflect/ParameterizedType.html

Google的Gson库定义了一个TypeToken类,它允许简单地生成参数化类型,并使用它以通用友好的方式对具有复杂参数化类型的json对象进行规范.在您的示例中,您将使用:

Type typeOfListOfFoo = new TypeToken<List<Foo>>(){}.getType()
Run Code Online (Sandbox Code Playgroud)

我打算发布到TypeToken和Gson类javadoc的链接,但Stack Overflow不会让我发布多个链接因为我是新用户,你可以使用Google搜索轻松找到它们

  • 有了这个,我能够创建一个具有通用 E 的类,然后使用 `clzz = new TypeToken&lt;E&gt;(){}.getRawType();` 稍后使用 `clzz.getEnumConstants()` 迭代类似结构的枚举,然后最后使用反射来调用成员方法,如 `Method method = clzz.getDeclaredMethod("getSomeFoo");` 太棒了!谢谢你! (2认同)

sla*_*ent 49

你可以用双重角色来管理它:

@SuppressWarnings("unchecked") Class<List<Foo>> cls = (Class<List<Foo>>)(Object)List.class

  • 你可以使用`Class <?>`:`(Class <List <Foo >>)(Class <?>)List.class` (10认同)
  • 通过将第二个强制转换为`Object`更改为`Class`,您可以节省(无意义的)已检查的运行时强制转换的开销. (2认同)
  • @Clashsoft使用“ Class”代替“ Object”,正如您所建议的那样,似乎更有意义,但它并没有消除对@SuppressWarnings(“ unchecked”)`注释的需要,它甚至添加了新的警告:`Class is原始类型。泛型类型Class &lt;T&gt;的引用应参数化` (2认同)
  • 这个答案完全没有意义。OP 想要参数化类路径的原因是因为他收到了一个“unchecked”警告。这个答案不会改变/改善任何一个。OP 甚至在他的问题中表示他不想使用“SuppressWarnings”...... (2认同)

Jim*_*son 6

为了阐述cletus的答案,在运行时删除了泛型类型的所有记录.泛型仅在编译器中处理,用于提供额外的类型安全性.它们实际上只是简写,允许编译器在适当的位置插入类型转换.例如,以前您必须执行以下操作:

List x = new ArrayList();
x.add(new SomeClass());
Iterator i = x.iterator();
SomeClass z = (SomeClass) i.next();
Run Code Online (Sandbox Code Playgroud)

List<SomeClass> x = new ArrayList<SomeClass>();
x.add(new SomeClass());
Iterator<SomeClass> i = x.iterator();
SomeClass z = i.next();
Run Code Online (Sandbox Code Playgroud)

这允许编译器在编译时检查您的代码,但在运行时它仍然看起来像第一个示例.

  • 在我看来,这只是意味着 Sun 以平庸的方式实现了泛型,希望 Oracle 有一天能解决这个问题。C# 的泛型实现要好得多(Anders 是上帝般的) (2认同)

ave*_*rin 5

您可以使用辅助方法来删除@SuppressWarnings("unchecked")整个类。

@SuppressWarnings("unchecked")
private static <T> Class<T> generify(Class<?> cls) {
    return (Class<T>)cls;
}
Run Code Online (Sandbox Code Playgroud)

然后你可以写

Class<List<Foo>> cls = generify(List.class);
Run Code Online (Sandbox Code Playgroud)

其他用法示例是

  Class<Map<String, Integer>> cls;

  cls = generify(Map.class);

  cls = TheClass.<Map<String, Integer>>generify(Map.class);

  funWithTypeParam(generify(Map.class));

public void funWithTypeParam(Class<Map<String, Integer>> cls) {
}
Run Code Online (Sandbox Code Playgroud)

但是,由于它很少真正有用,并且该方法的使用会破坏编译器的类型检查,因此我不建议在可公开访问的地方实现它。


Mar*_*234 5

Java泛型常见问题解答以及 cletus 的回答听起来好像没有任何意义Class<List<T>>,但真正的问题是这是极其危险的:

@SuppressWarnings("unchecked")
Class<List<String>> stringListClass = (Class<List<String>>) (Class<?>) List.class;

List<Integer> intList = new ArrayList<>();
intList.add(1);
List<String> stringList = stringListClass.cast(intList);
// Surprise!
String firstElement = stringList.get(0);
Run Code Online (Sandbox Code Playgroud)

cast()使得它看起来好像很安全,但实际上根本不安全。


虽然我不明白哪里不能有List<?>.class=,Class<List<?>>因为当你有一个根据参数的泛型类型确定类型的方法时,这会非常有用Class

因为getClass()JDK-6184881请求切换到使用通配符,但是看起来不会(很快)执行此更改,因为它与以前的代码不兼容(请参阅此评论)。