【问题标题】:Why is using Collection<String>.class illegal?为什么使用 Collection<String>.class 非法?
【发布时间】:2011-02-14 06:28:12
【问题描述】:

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

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

您可以将此字段指定为:

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

但是,这会产生错误:

标记“>”的语法错误,此标记后应为无效

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

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

也不行,生成:

类型不匹配:无法从 Class&lt;Test.StringCollection&gt; to Class&lt;Collection&lt;String&gt;&gt; 转换

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

【问题讨论】:

标签: java


【解决方案1】:

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

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

【讨论】:

  • 泛型是编译期间的约束,在运行时大多不可用。但是,我不打算在运行时使用泛型类型参数。例如, ArrayList.class.newInstance() 似乎完全可以接受。我找不到这个模型失败的地方,我能想到的所有例子都没有介绍需要有类型参数的运行时类信息。我正在寻找一个类文字会失败的示例,因为它需要运行时信息...
  • 有关编译时的完整类型信息,您可以查看 Mirrors API - 我现在不记得链接了。
  • 我不认为这是关于编译时的类型(在类意义上)信息,这只是关于类型约束,即类型参数。
【解决方案2】:

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

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

但是,我的主要问题是我也不能用扩展 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 ); 
}

也就是说我还没有找到根本原因是Class&lt;A&lt;S&gt;&gt;.class 无效的原因。擦除和界限似乎都不需要这是无效的。

【讨论】:

    【解决方案3】:

    泛型是不变的。

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

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

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

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

    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>>
    

    至于运行时未具体化的情况,您似乎掌握得很好,但无论如何这里有一个引用,来自Java Tutorials on GenericsThe Fine PrintA Generic Class is Shared by All Its调用

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

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

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

    也就是说,没有List&lt;String&gt;.classList&lt;Integer&gt;.class 这样的东西;只有List.class

    这也体现在JLS 15.8.2 Class Literals

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

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

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

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

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

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

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

    【讨论】:

    猜你喜欢
    • 2011-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-28
    • 1970-01-01
    • 2011-08-23
    • 1970-01-01
    相关资源
    最近更新 更多