java 为什么使用 Collection<String>.class 是非法的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2745193/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 22:38:50  来源:igfitidea点击:

Why is using Collection<String>.class illegal?

java

提问by Peter Kriens

I am puzzled by generics. You can declare a field like:

我对泛型感到困惑。您可以声明一个字段,如:

Class<Collection<String>> clazz = ...

It seems logical that you could assign this field with:

您可以为该字段分配以下内容似乎合乎逻辑:

Class<Collection<String>> clazz = Collection<String>.class;

However, this generates an error:

但是,这会产生错误:

Syntax error on token ">", void expected after this token

标记 ">" 上的语法错误,此标记后预期无效

So it looks like the .classoperator does not work with generics. So I tried:

所以看起来.class运算符不适用于泛型。所以我试过:

  class A<S> { }
  class B extends A<String> { }
  Class<A<String>> c = B.class;

Also does not work, generates:

也不起作用,生成:

Type mismatch: cannot convert from Class<Test.StringCollection> to Class<Collection<String>>

类型不匹配:无法从 Class<Test.StringCollection> to Class<Collection<String>>

Now, I really fail to see why this should not work. I know generic types are not reified, but in both cases it seems to be fully type safe without having access to runtime generic types. Anybody an idea?

现在,我真的不明白为什么这不应该起作用。我知道泛型类型没有具体化,但在这两种情况下,它似乎是完全类型安全的,而无需访问运行时泛型类型。有人有想法吗?

回答by polygenelubricants

Generics are invariant.

泛型是不变的。

Object o = "someString"; // FINE!
Class<Object> klazz = String.class; // DOESN'T COMPILE!
// cannot convert from Class<String> to Class<Object>

Depending on what it is that you need, you may be able to use wildcards.

根据您的需要,您可以使用通配符。

Class<? extends Number> klazz = Integer.class; // FINE!

Or perhaps you need something like this:

或者你可能需要这样的东西:

Class<List<String>> klazz =
   (Class<List<String>>) new ArrayList<String>().getClass();
// WARNING! Type safety: Unchecked cast from
//   Class<capture#1-of ? extends ArrayList> to Class<List<String>>


As for the non-reified at run-time case, you seem to have a good grasp, but here's a quote anyway, from the Java Tutorials on Generics, The Fine Print: A Generic Class is Shared by All Its Invocations:

至于在运行时未具体化的情况,您似乎掌握得很好,但无论如何,这里有一个引述,来自Java 泛型教程精美印刷泛型类由其所有调用共享

What does the following code fragment print?

List <String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());

You might be tempted to say false, but you'd be wrong. It prints true, because all instances of a generic class have the same run-time class, regardless of their actual type parameters.

下面的代码片段打印什么?

List <String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());

你可能想说false,但你错了。它打印true,因为泛型类的所有实例都具有相同的运行时类,而不管它们的实际类型参数如何。

That is, there's no such thing as List<String>.classor List<Integer>.class; there's only List.class.

也就是说,没有List<String>.classor之类的东西List<Integer>.class;只有List.class.

This is also reflected in the JLS 15.8.2 Class Literals

这也反映在JLS 15.8.2 类文字中

A class literal is an expression consisting of the name of a class, interface, array, or primitive type, or the pseudo-type void, followed by a .and the token class.

类字面量是一个表达式,由类、接口、数组或原始类型的名称或伪类型 void 组成,后跟 a.和标记class

Note the omission of any allowance for generic type parameters/arguments. Furthermore,

请注意省略了对泛型类型参数/参数的任何允许。此外,

It is a compile time error if any of the following occur:

  • The named type is a type variable or a parameterized type, or an array whose element type is a type variable or parameterized type.

如果发生以下任一情况,则为编译时错误:

  • 命名类型是类型变量或参数化类型,或元素类型为类型变量或参数化类型的数组。

That is, this also doesn't compile:

也就是说,这也不能编译:

void <T> test() {
    Class<?> klazz = T.class; // DOESN'T COMPILE!
    // Illegal class literal for the type parameter T
}

Basically you can't use generics with class literals, because it just doesn't make sense: they're non-reified.

基本上你不能将泛型与类文字一起使用,因为它没有意义:它们是非具体化的。

回答by Little Bobby Tables

I agree with the other answers, and would like to explain one point further:

我同意其他答案,并想进一步解释一点:

Class objects represent classes that are loaded into the JVM memory. Each class object is actually an in-memory instance of a .classfile. Java generics are notseparate classes. They are just a part of the compile-time type-checking mechanism. Therefore, they have no run-time representation in a class object.

类对象表示加载到 JVM 内存中的类。每个类对象实际上是一个.class文件的内存实例。Java 泛型不是单独的类。它们只是编译时类型检查机制的一部分。因此,它们在类对象中没有运行时表示。

回答by Peter Kriens

There seems to be a lack in class literals in Java, there is no way to create class literals with generic information while this can be useful in certain cases. Therefore, the following code cannot be called because it is impossible to provide the class literal

Java 中似乎缺少类文字,无法使用通用信息创建类文字,而这在某些情况下很有用。因此,无法调用以下代码,因为无法提供类字面量

class A<S> {}
<S> A<S> foo( Class<A<S>> clazz ) {}
A<String> a = foo( A<String>.class ) // error

However, my main problem was I could also not call it with a class B that extended A. This was caused by the invariance restrictions. This was solved by using a wildcard:

但是,我的主要问题是我也不能用扩展 A 的 B 类来调用它。这是由不变性限制引起的。这是通过使用通配符解决的:

class A<S> {}
class B extends A<String> {}     
<S> A<S> foo( Class<? extends A<S>> clazz ) { return null; }
void test () {
    A<String> s = foo( B.class ); 
}

That said I have not found a reason what the underlying reason is that Class<A<S>>.classis invalid. Neither erasure nor bounds seem require that this is invalid.

也就是说,我还没有找到根本原因Class<A<S>>.class是无效的原因。擦除和边界似乎都不要求这是无效的。