[英]Why bounds work so strange in Java?
I'm using Java 8. During training to passing Java OCP 8 I find some snippets of code that I don't understand and want to know, why it so strange for me. 我正在使用Java 8.在通过Java OCP 8的培训期间,我发现了一些我不理解和想知道的代码片段,为什么它对我来说如此奇怪。
I have next hierarchy: 我有下一个层次结构:
class A {}
class B extends A {}
class C extends B {}
The first one, this code is work: 第一个,这个代码是工作:
List<?> list1 = new ArrayList<A>() {
{
add(new A());
}
};
But next code doesn't work, compilation error: 但是下一个代码不起作用,编译错误:
list1.add(new A());
So, why we can't add new record in this way? 那么,为什么我们不能以这种方式添加新记录呢?
The second one, this code is work: 第二个,这个代码是工作:
List<? extends A> list2 = new ArrayList<A>() {
{
add(new A());
add(new B());
add(new C());
}
};
But next code doesn't work, compilation error: 但是下一个代码不起作用,编译错误:
list2.add(new A());
list2.add(new B());
list2.add(new C());
And the last one, this code is work: 最后一个,这个代码是工作:
List<? super B> list3 = new ArrayList<A>() {
{
add(new A());
add(new B());
add(new C());
}
};
But in the next code, when we adding new A() , compilation error: 但是在下一个代码中,当我们添加新的A()时 ,编译错误:
list3.add(new A()); // compilation error
list3.add(new B());
list3.add(new C());
Thanks for your answers! 谢谢你的回答!
This is a compilation error designed to enforce type safety. 这是一个旨在强制执行类型安全的编译错误。 If the compiler allowed you to do it, imagine what could happen:
如果编译器允许你这样做,想象一下会发生什么:
For issue 1, once the object list1
has been declared, the compiler only considers the declared type, which is List<?>
and ignores the fact that it was most recently assigned to an ArrayList<A>
. 对于问题1,一旦声明了对象
list1
,编译器只考虑声明的类型,即List<?>
并忽略它最近被分配给ArrayList<A>
的事实。
List<?> list1 = ...; // The compiler knows list1 is a list of a certain type
// but it's not specified what the type is. It could be
// a List<String> or List<Integer> or List<Anything>
list1.add(new A()); // What if list1 was e.g. a List<String>?
But: 但:
List<?> list1 = new ArrayList<A>() {
{
add(new A());
}
};
Here, you are assigning to list1
an expression. 在这里,您将为
list1
分配一个表达式。 The expression itself, ie everything after =
, doesn't use ?
表达式本身,即
=
之后的所有内容,都不使用?
, and is in fact an anonymous class that extends ArrayList<A>
and has an initializer block that calls add(new A())
which is ok. ,实际上是一个扩展
ArrayList<A>
的匿名类,并有一个初始化块,调用add(new A())
,这是好的。
The second issue (with list2) has the same cause. 第二个问题(与list2)有相同的原因。
In the third issue, 在第三期,
List<? super B> list3 = new ArrayList<A>() {
{
add(new A());
add(new B());
add(new C());
}
};
list3.add(new A()); // compilation error
The compiler sees list3
as a List<? super B>
编译器将
list3
视为List<? super B>
List<? super B>
. List<? super B>
。 This means the generic parameter can be B
or its superclass, A
. 这意味着泛型参数可以是
B
或其超类A
What if it's a List<B>
? 如果它是
List<B>
怎么办? You can't add an A
to a List<B>
; 您无法将
A
添加到List<B>
; therefore the compiler rejects this code. 因此编译器拒绝此代码。
The short answer ("why it is strange") is that certain Java short-hand notations make two pieces of code look very similar when they are really very different. 简短的回答(“为什么它很奇怪”)是某些Java简写符号使两段代码看起来非常相似,当它们真的非常不同时。
So it can seem like "if this works, this should also work", but it's not so because important differences in the two bits of code are obscured. 所以看起来“如果这样做,这也应该有效”,但事实并非如此,因为两位代码中的重要差异是模糊的。
Let's look at a few pieces of code separately: 让我们分别看几段代码:
public interface ListFactory {
List<?> getList();
}
So someone's going to give us a way to request a List<?>
. 所以有人会给我们一个请求
List<?>
。 We can use it like this: 我们可以像这样使用它:
List<?> list1 = myListFactory.getList();
But if we do 但如果我们这样做
list1.add(new A());
the compiler can't prove this is legal, because it depends on whether we happened to get a ListFactory implementation that returns a List<A>
, or maybe a List<String>
. 编译器无法证明这是合法的,因为它取决于我们是否碰巧得到一个返回
List<A>
或List<String>
的ListFactory实现。
What if we replace the above with 如果我们用以上代替上述内容怎么办
List<?> list1 = new SpecialList();
list1.add(new A());
This is a little closer to your original code; 这比原始代码更接近; but to the compiler the same problem exists: when it evaluates
list1.add(new A());
但是对于编译器存在同样的问题:当它评估
list1.add(new A());
it doesn't look for clues in the history of assignments to list1. 它不会在list1的赋值历史中寻找线索。 It only knows that the compile-time reference type of list1 is
List<?>
, so if list1.add(new A());
它只知道list1的编译时引用类型是
List<?>
,所以如果list1.add(new A());
was illegal before, it's still illegal here. 以前是非法的,在这里仍然是非法的。
(At a glance this may feel like a shortcoming of the compiler, but I think it's not; it generally isn't practical or desirable for the compiler to try and know any more than the reference type directly tells it. If we'd wanted to refer to our object in a way that allows add(new A())
we'd use a different reference type - eg List<A> list2 = new SpecialList();
.) (乍一看,这可能感觉像是编译器的一个缺点,但我认为不是;编译器通常不会尝试和了解直接告诉它的参考类型,这通常是不实际或不可取的。如果我们想要的话以允许
add(new A())
的方式引用我们的对象,我们使用不同的引用类型 - 例如List<A> list2 = new SpecialList();
)
The above implies that we have a SpecialList.java; 以上暗示我们有一个SpecialList.java; let's take a look at it:
让我们来看看它:
public class SpecialList extends ArrayList<A> {
public SpecialList() {
add(new A());
}
}
This might seem a little silly, but it's probably no surprise that there's nothing wrong with it as far as the compiler is concerned (as long as A is defined and java.util.ArrayList is imported). 这可能看起来有些愚蠢,但就编译器而言,只要定义了A并导入了java.util.ArrayList,它就不足为奇了。
Note that add(new A());
注意
add(new A());
in this case is shorthand for this.add(new A());
在这种情况下,
this.add(new A());
简写this.add(new A());
. 。 In the SpecialList() constructor,
this
is a reference of type SpecialList - which is declared to extend ArrayList<A>
- and certainly we can add(new A())
to a subclass of ArrayList<A>
. 在SpecialList()构造函数中,
this
是一个类型为SpecialList的引用 - 它被声明为扩展ArrayList<A>
- 当然我们可以add(new A())
到ArrayList<A>
的子类中。
So now we have all the pieces to make something like your original code: 所以现在我们有了所有的东西来制作像你的原始代码:
List<?> list1 = new ArrayList<A>() {
{
add(new A());
}
}
list1.add(new A());
Lines 3 and 6 here look very similar now because of the Java syntactic sugar we've applied. 由于我们应用的Java语法糖,现在第3行和第6行看起来非常相似。 But line 3 is really like our SpecialList() example - the reference type through which add() is invoked is an anonymous subclass of
ArrayList<A>
. 但第3行与我们的SpecialList()示例非常相似 - 调用add()的引用类型是
ArrayList<A>
的匿名子类。 Line 6, though, is pretty much what it appears to be - and so it fails for the same reason it did in the first couple examples. 然而,第6行几乎就是它的样子 - 因此它失败的原因与它在前几个例子中所做的相同。
Similar analysis will explain the other weird distinctions you're seeing. 类似的分析将解释你所看到的其他奇怪的区别。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.