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

时间:2014-05-07 20:53:51

标签: java diamond-operator

class A {}

class B extends A {}

class Holder<T> {

    T object;

    Holder(T object) {
        this.object = object;
    }
}

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

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

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

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

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

2 个答案:

答案 0 :(得分:8)

首先观察:

Holder<B> h = new Holder<>(new B());

使用Java 8和Java 7进行编译,并在该场景中创建Holder<B>。因此,使用带有参数的构造函数的<>是可以的。

但是:

Holder<A> h = new Holder<>(new B());

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

  

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

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

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

  

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

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

答案 1 :(得分:1)

你会得到

Type mismatch: cannot convert from Holder<B> to Holder<A>

原因是,

new Holder<>(new B()); 

将调用构造函数Holder(T object),并将<T>的类型设置为B,与您的左手作业Holder<A> holder匹配。

如果您将左手类型更改为Holder<B> holderHolder<? extends A> holder,即使没有在右侧<>括号中输入类型,它也能正常工作。

请记住BA的子类,因此A可以保留B的引用,这就是

的原因
Holder<A> holder = new Holder<A>(new B());

OR

Holder<? extends A> holder = new Holder<>(new B());

有效