【问题标题】:incompatible types and fresh type-variable不兼容的类型和新的类型变量
【发布时间】:2013-12-12 12:44:38
【问题描述】:

我收到以下编译消息:

[javac]   ... error: incompatible types
[javac]         exceptionClassHolder = new Holder<>( (new Exception()).getClass() );
[javac]                                ^
[javac]   required: Holder<Class<? extends Exception>>
[javac]   found:    Holder<Class<CAP#1>>
[javac]   where CAP#1 is a fresh type-variable:
[javac]     CAP#1 extends Exception from capture of ? extends Exception
[javac] 1 error

在我看来,根据消息,一切都应该是正确的。 CAP#1 确实扩展了异常。那么上面的信息应该怎么理解呢? SSCCE 下面(最初没有发布,因为我希望在一般情况下理解错误消息本身):

class Holder<T> {
    public T t;
    public Holder(T t) {
       this.t = t;
    }
}

public class FooMain {
    public static void main(String args[]) throws Exception {
        Holder<Class<? extends Exception>> exceptionClassHolder;
        exceptionClassHolder = new Holder<>( (new Exception()).getClass() );
    }
}

【问题讨论】:

  • @Bohemian 好的,我会先尝试在更简单的情况下重现它

标签: java generics


【解决方案1】:

不幸的是,现有的答案并没有解释这里发生了什么。首先,解决方案是简单地指定Holder的类型参数:

Holder<Class<? extends Exception>> exceptionClassHolder;
exceptionClassHolder =
        new Holder<Class<? extends Exception>>(new Exception().getClass());

您的版本不起作用的原因是new Exception().getClass() 返回一个Class&lt;? extends Exception&gt;,其中? 是一个wildcard capture(在编译器错误消息中称为CAP#1)。由于您将“菱形运算符”与 new Holder&lt;&gt; 一起使用,因此编译器将 Class&lt;CAP#1 extends Exception&gt; 推断为 T,因此 Holder&lt;Class&lt;CAP#1 extends Exception&gt;&gt; 是所创建对象的类型。

但是,这与您声明的 Holder&lt;Class&lt;? extends Exception&gt;&gt; 类型不匹配。它使用nested wildcard,它不捕获:虽然CAP#1 extends Exception某些特定类型 扩展Exception,嵌套? extends Exception 代表字面上任何类型 扩展Exception.

虽然Class&lt;CAP#1 extends Exception&gt;Class&lt;? extends Exception&gt; 的子类型,但Holder&lt;Class&lt;CAP#1 extends Exception&gt;&gt; 不是Holder&lt;Class&lt;? extends Exception&gt;&gt; 的子类型,因为generics aren't covariant,所以分配失败。

通过为T 手动指定Class&lt;? extends Exception&gt;,您可以帮助编译器避免这个“陷阱”。

在这些帖子中查看我的类似答案:

【讨论】:

  • +1 用于扩展说明。但是我不明白您为什么说“首先,解决方案是简单地为 Holder 指定类型参数”。为什么?在 Java 7 中,它应该由编译器推断。无论如何,如果不是,它应该在编译时崩溃,这不是 OP 的问题。
  • 它是由编译器推断的,但不是“正确”的——推断优先于构造函数参数而不是变量。它在编译时确实失败了。
  • 有趣。我只查看了 Object.getClass() 的返回类型,而不是文档的文本,它确实说返回的实际类型是 Class&lt;? extends Exception&gt;。我是否认为这是一个由编译器专门处理的非常特殊的情况,并且不可能像 getClass() 那样对常规 Java 代码进行处理?
  • @JBNizet 完全正确,抱歉没有早点详细说明。
  • 我仍然更喜欢 Joop Eggen 的解决方案,因为它使得使用菱形运算符成为可能,而且丑陋只隐藏在一个地方。但是,我接受您的回答,因为我的问题的措辞是为了引出有关理解消息和错误的信息,而不是关于应用什么解决方案的信息。
【解决方案2】:
    Holder<? extends Class<? extends Exception>> exceptionClassHolder;
    exceptionClassHolder = new Holder<>( (new Exception()).getClass() );

原因是

  1. 因为你使用的不是Exception.class而是一个Exception对象,java认为? extends Exception是必需的。
  2. 同样适用于getClass(),同样需要? extends Class,尽管类是最终的。

这肯定有一天会被简化。

【讨论】:

  • 这是不必要的——看我的回答。
  • @PaulBellora 我的解决方案 钻石运算符一起工作(如质疑)。阅读您的出色答案(我的+1),人们会觉得钻石操作员不应该工作。 如您所见,我有编程语言/编译器构建背景,所以您可以详细说明一下。 谢谢。
  • 你说得对,菱形运算符在分配给Holder&lt;? extends Class&lt;? extends Exception&gt;&gt; 时起作用——这是因为该类型可以从Holder&lt;Class&lt;CAP#1 extends Exception&gt;&gt; 分配,与Holder&lt;Class&lt;? extends Exception&gt;&gt; 不同。类型推断过程仍然相同 - 编译器优先考虑构造函数参数并推断Class&lt;CAP#1 extends Exception&gt;T
  • 我想指出,使用Holder&lt;? extends Class&lt;? extends Exception&gt;&gt; 是不必要的,因为它将exceptionClassHolder 限制为仅是"producer",这可能是不可取的。
猜你喜欢
  • 1970-01-01
  • 2012-08-06
  • 2020-02-18
  • 2019-11-08
  • 1970-01-01
  • 1970-01-01
  • 2015-04-26
  • 1970-01-01
相关资源
最近更新 更多