当前位置:  开发笔记 > 编程语言 > 正文

Java 8 autoboxing +泛型:变量与方法的不同行为

如何解决《Java8autoboxing+泛型:变量与方法的不同行为》经验,为你挑选了2个好方法。

我找到了一段代码,从Java 7切换到Java 8后停止编译.它没有任何新的Java 8内容,如lambda或流.

我将有问题的代码缩小到以下情况:

GenericData g = new GenericData<>(1d);
Double d = g == null ? 0 : g.getData(); // type error!!!

您可能猜测GenericData构造函数有一个泛型类型的参数,该getData()方法只返回该泛型类型.(有关完整的源代码,请参阅下文.)

现在困扰我的是,在Java 7中,代码编译得很好,而使用Java 8,我得到以下错误:

CompileMe.java:20: error: incompatible types: bad type in conditional expression
Double d = g == null ? 0 : g.getData();
                       ^
int cannot be converted to Double

似乎Java 7能够从int - > double - > Double进行转换,但Java 8因尝试立即从int - > Double而失败.

我觉得特别有趣的是Java 8 确实接受代码时,我改变它getData()data,即访问GenericData通过变量本身而不是消气方法的价值:

Double d2 = g == null ? 0 : g.data; // now why does this work...

所以我在这里提出的两个问题是:

    为什么Java 8不会推断像Java 7这样的类型,并且在自动装箱double到Double之前将我的int转换为double?

    为什么这个问题只发生在泛型方法而不是泛型变量?

完整源代码:

public class CompileMe {
    public void foo() {
        GenericData g = new GenericData(1d);
        Double d = g == null ? 0 : g.getData(); // type error!!!
        Double d2 = g == null ? 0 : g.data; // now why does this work...
    }
}

class GenericData {
    public T data;
    public GenericData(T data) {
        this.data = data;
    }
    public T getData() {
        return data;
    }
}

要测试它,运行编译器如下:

javac -source 1.7 -target 1.7 CompileMe.java   # ok (just warnings)
javac -source 1.8 -target 1.8 CompileMe.java   # error (as described above)

最后如果重要:我运行Windows 8和Java 1.8.0_112(64位).



1> Holger..:

方法调用表达式的特殊之处在于它们可能是Poly表达式,受目标类型的限制.

请考虑以下示例:

static Double aDouble() {
    return 0D;
}
…
Double d = g == null ? 0 : aDouble();

这编译没有任何问题

static  T any() {
    return null;
}
…
Double d = g == null ? 0 : any();

这里,调用any()是一个Poly Expression,编译器必须推断T := Double.这再现了相同的错误.

这是第一个不一致.虽然你的方法getData()是指类型参数TGenericData,它不是一个通用的方法(有/应参与确定没有类型推断TDouble在这里.

JLS§8.4.4.通用方法

如果方法声明一个或多个类型变量,则该方法是通用的

getData() 不声明类型变量,它只使用一个.

JLS§15.12.方法调用表达式:

如果满足以下所有条件,则方法调用表达式是poly表达式:

...

由以下小节确定的要调用的方法是通用的(第8.4.4节),并且具有提及方法的类型参数中的至少一个的返回类型.

由于此方法调用不是多表达式,因此它应该像aDouble()调用的示例一样,而不是any().

但请注意§15.25.3:

请注意,引用条件表达式不必包含多重表达式作为操作数,以便成为多重表达式.它只是凭借其出现的上下文而是一个多义表达式.例如,在以下代码中,条件表达式是poly表达式,并且每个操作数都被视为在赋值上下文中定位Class:

Class choose(boolean b,
                              Class c1,
                              Class c2) {
    return b ? c1 : c2;
}

那么,它是引用条件表达式还是数值条件表达式?

§15.25.条件运算符?:说:

根据第二和第三操作数表达式分类有三种条件表达式:布尔条件表达式,数值条件表达式引用条件表达式.分类规则如下:

如果第二个和第三个操作数表达式都是布尔表达式,则条件表达式是布尔条件表达式.
...

如果第二个和第三个操作数表达式都是数值表达式,则条件表达式是数字条件表达式.
为了对条件进行分类,以下表达式是数字表达式:

表达独立形式(第15.2节),其类型可转换为数字类型(§4.2,§5.1.8).

带括号的数字表达式(第15.8.5节).

可转换为数字类型的类的类实例创建表达式(第15.9节).

方法调用表达式(第15.12节),其中所选择的最具体方法(第15.12.2.5节)具有可转换为数字类型的返回类型.

数字条件表达式.

否则,条件表达式是引用条件表达式.

因此,根据这些规则,不排除泛型方法调用,所有显示的条件表达式都是数字条件表达式并且应该起作用,因为只有" 否则 "它们被认为是引用条件表达式.我测试的Eclipse版本编译了所有这些版本而没有报告任何错误.

这导致了奇怪的情况,对于这种any()情况,我们需要目标类型来找出它具有数字返回类型并推断条件是数字条件表达式,即独立表达式.请注意,对于布尔条件表达式,有以下注释:

请注意,对于泛型方法,这是在实例化方法的类型参数之前的类型.

但是对于数字条件表达式,没有这样的音符,无论是有意还是无意.

但正如所说,这只适用于该any()示例,因为该getData()方法不是通用的.


请注意,但我已经两次阅读了你的答案,仍然没有明显的线索,为什么这仍然失败了jdk-8编译器.底线这是一个jdk-8编译器bug吗?
@Holger发现[错误ID:JDK-8162708](http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8162708) - 似乎与您的观察相符

2> Hulk..:

这似乎是Oracle编译器的一个已知问题:错误ID:JDK-8162708

引用:

问题描述:
如果在泛型类中有一个声明如下的方法:

class Foo {
  public T getValue() {
    // returns a value ...
  }
}

你可以在三元运算符中调用上面的方法,如下所示

Foo foo = new Foo<>();
Float f = new Random().nextBoolean() ? foo.getValue() : 0f;

你从javac 1.8编译器得到一个语法错误.

但上面的代码编译时没有错误和警告与javac 1.7和1.9.

解决方案:尚未解决

受影响的版本:8

来自评论:

此问题仅适用于8u,7和9中没有问题

推荐阅读
mobiledu2402851173
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有