Java 7钻石操作符和派生类的初始化

use*_*844 6 java diamond-operator

class A {}

class B extends A {}

class Holder<T> {

    T object;

    Holder(T object) {
        this.object = object;
    }
}
Run Code Online (Sandbox Code Playgroud)

有一个Holder类来保存一些使用泛型创建的对象.在main()中,当使用菱形运算符初始化时,它不会编译(Java 7),派生类传递给Holder的构造函数(需要A /找到B):

public static void main(String[] args) {    
    Holder<A> holder = new Holder<>(new B());        
}
Run Code Online (Sandbox Code Playgroud)

但是,如果在右侧部分指定了基本类型,它将编译并运行:

public static void main(String[] args) {
    Holder<A> holder = new Holder<A>(new B());
}
Run Code Online (Sandbox Code Playgroud)

为什么?钻石操作员是否使用与左侧相同的类型参数定义赋值的右侧部分?

ass*_*ias 8

首先观察:

Holder<B> h = new Holder<>(new B());
Run Code Online (Sandbox Code Playgroud)

使用Java 8和Java 7编译,并Holder<B>在这种情况下创建.所以使用<>带有参数的构造函数是可以的.

然而:

Holder<A> h = new Holder<>(new B());
Run Code Online (Sandbox Code Playgroud)
  • Java 7首先评估右侧,确定它是a Holder<B>并给出编译错误,因为a Holder<B>无法转换为aHolder<A>.
  • Java 8更进了一步,推断出它new Holder<A>(new B())可以实际工作并自动神奇地完成.这要归功于一个名为"目标类型"的新功能 - 请参阅教程的最后一部分,了解类型推断的概述.

更详细地说,Java 8的改进是由于poly表达式的引入(强调我的):

独立表达式的类型可以完全根据表达式的内容来确定; 相反,poly表达式的类型可能受表达式的目标类型(§5(转换和上下文))的影响.

这是Java 8的一个非常强大的功能(Java 7仅提供不考虑表达式上下文的独立表达式).

泛型类实例创建是一个多义表达式,JLS#15.9解释了(强调我的):

如果类实例创建表达式使用菱形表示类的类型参数,它就出现在赋值上下文或调用上下文中(第5.2节,第5.3 节),它是一个多表达式(第15.2 节).否则,它是一个独立的表达式.

由于这个新规则,Java 8允许您使用上面的第二个表单并自动推断new B()应该被视为A(加宽引用转换)并且您打算Holder<A>在该上下文中创建一个.