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());
}
为什么呢?钻石操作员是否使用与左侧相同的类型参数定义赋值的右侧部分?
答案 0 :(得分:8)
首先观察:
Holder<B> h = new Holder<>(new B());
使用Java 8和Java 7进行编译,并在该场景中创建Holder<B>
。因此,使用带有参数的构造函数的<>
是可以的。
但是:
Holder<A> h = new Holder<>(new B());
Holder<B>
并因a Holder<B>
can't be converted into a Holder<A>
而发出编译错误。new Holder<A>(new B())
实际上可以正常工作并自动神奇地完成。这要归功于一项名为“目标类型”的新功能 - 请参阅the tutorial on type inference的最后一部分以获取概述。更详细地说,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> holder
或Holder<? extends A> holder
,即使没有在右侧<>
括号中输入类型,它也能正常工作。
请记住B
是A
的子类,因此A
可以保留B
的引用,这就是
Holder<A> holder = new Holder<A>(new B());
OR
Holder<? extends A> holder = new Holder<>(new B());
有效