【问题标题】:Why does Eclipse Compiler lose fixed type parameter?为什么 Eclipse 编译器会丢失固定类型参数?
【发布时间】:2015-06-24 06:57:49
【问题描述】:

我很难为这个问题找到一个合适的标题,因为我观察到的现象很奇怪。因此,我跳过字面解释我的问题,而是向您展示一些(希望是)自描述代码。考虑以下参数化类:

public class GenericOptional<T> {

    public GenericOptional(T someValue) {}

    public T getValue() { return null; }

    public Optional<String> getOptionalString() { return Optional.empty(); }
}

我想强调的是getOptionalString()方法的返回类型Optional&lt;String&gt;不依赖于类型参数T

现在看看下面的代码,它是在 Eclipse Luna 4.4.2 中使用 Java 8u45 编译的:

public static void main(String[] args) {
    Object obj = new GenericOptional<>(Boolean.TRUE);
    GenericOptional go = (GenericOptional) obj;
    Optional os = go.getOptionalString();
}

局部变量os 的类型为Optional,没有类型参数 StringEclipse 编译器丢失了关于固定type-parameter 的信息。有谁知道为什么?

现在看第二个代码示例:

public static void main(String[] args) {
    Object obj = new GenericOptional<>(Boolean.TRUE);
    GenericOptional<?> go = (GenericOptional) obj;
    Optional<String> os = go.getOptionalString();
}

通过将局部变量go 声明为GenericOptional&lt;?&gt;,方法getOptionalString() 现在的返回类型正如预期的那样是Optional&lt;String&gt;

谁能解释一下这种行为?

【问题讨论】:

    标签: eclipse generics java-8 optional type-parameter


    【解决方案1】:

    您正面临raw types 的行为。当您使用原始类型时,无论成员的泛型签名与类的类型参数之间是否存在联系,泛型都会有效地完全关闭。

    这背后的原因是 原始类型 是一个仅向后兼容预泛型代码的功能。所以要么有泛型,要么没有。

    如果Generic方法不依赖类的实际类型参数,问题很容易解决:

    GenericOptional<?> go = (GenericOptional<?>) obj;
    Optional<String> os = go.getOptionalString();
    

    使用&lt;?&gt; 意味着“我不知道实际的类型参数,我不在乎,但我正在使用通用类型检查”。

    【讨论】:

    • 感谢您的回答。 Kocko 给出了相同的答案,我不知道如何给你们两个正确/最好的认可。因此,由于您首先回答了问题,即使您没有提供示例代码来支持您的解释,我也会给您“最有帮助”的认可。如果您认为 Kocko 的回答对想了解其工作原理的人更有价值,请拒绝,我会将“最有帮助”的批准转给 Kocko。
    【解决方案2】:

    这不是关于 Eclipse 或任何东西,而是关于原始类型。

    让我们回顾一下这个sn-p:

    public static void main(String[] args) {
        Object obj = new GenericOptional<>(Boolean.TRUE);
        GenericOptional go = (GenericOptional) obj;
        Optional os = go.getOptionalString();
    }
    

    在这里,您正在创建GenericOptionalraw 实例,这意味着类型参数信息将被完全关闭。因此,实例化 raw GenericOptional 意味着该实例将公开以下方法:

    public class GenericOptional {
    
        public GenericOptional(Object someValue) {}
    
        public Object getValue() { return null; }
    
        public Optional getOptionalString() { return Optional.empty(); }
    }
    

    但是,如果我们现在查看第二个 sn-p

    public static void main(String[] args) {
        Object obj = new GenericOptional<>(Boolean.TRUE);
        GenericOptional<?> go = (GenericOptional) obj;
        Optional<String> os = go.getOptionalString();
    }
    

    我们可以看到您正在创建GenericOptional 的通用实例。即使它的类型参数是&lt;?&gt;,编译器也不会关闭关心类型参数,所以实例会暴露getOptionalString()方法参数化,像这样:

    public Optional<String> getOptionalString() { return Optional.empty(); }
    

    【讨论】:

    • 感谢 Kocko 的精彩解释。请参阅我在 Holger 的回答中添加的评论,为什么我倾向于给予他“最有帮助”的认可。希望您能接受这个决定。
    猜你喜欢
    • 2015-06-20
    • 2011-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多