英文:
Assignment of a list of generics implementing an interface to a list of the same interface
问题
这个问题涉及接口与实现该接口的类之间的关系。我看不出这个链接或这个链接是如何回答这个问题的。
我创建了一个名为Boxed
的接口和一个实现该接口的抽象泛型类Box
。然后我创建了两个具体的类IntegerBox
和StringBox
。接着,我创建了一个元素列表,这些元素扩展了Box
,其中包括一个IntegerBox
值和一个StringBox
值。目前为止一切顺利。
现在,我想将List<? extends Box>
分配给List<Boxed>
。我期望这是有效的,因为任何扩展Box
的类也实现了Boxed
。但是编译器不允许我这样做。这是错误信息:
main.java:29: 错误: 无法将List<CAP#1>转换为List<Boxed>
List<Boxed> lb2 = laeb; // 尽管每个扩展了Box的值都实现了Boxed,但不能编译通过
^
其中,CAP#1是一个新的类型变量:
CAP#1继承自Box,来自于对? extends Box的捕获
1个错误
我可以复制列表:
List<? extends Box> laeb = List.of(new IntegerBox(42), new StringBox("answer"));
List<Boxed> lb1 = new ArrayList<>(laeb);
类型为? extends Box
的每个元素都用于创建Boxed
类型的值。但是这个赋值报告了不兼容的类型。为什么呢?
import java.util.List;
import java.util.ArrayList;
public class main {
static interface Boxed { }
static abstract class Box<T> implements Boxed {
T content;
Box(T content) { this.content = content; }
}
static class IntegerBox extends Box<Integer> {
IntegerBox(Integer content) { super(content); }
}
static class StringBox extends Box<String> {
StringBox(String content) { super(content); }
}
public static void main(String... arguments) throws Exception {
IntegerBox i = new IntegerBox(42);
StringBox s = new StringBox("answer");
List<? extends Box> laeb = List.of(i, s);
Boxed b0 = i; // => IntegerBox兼容Boxed
Boxed b1 = s; // => StringBox兼容Boxed
List<Boxed> lb1 = new ArrayList<>(laeb); // List<Boxed>可以通过"? extends Box"的值来创建
List<Boxed> lb2 = laeb; // 尽管每个扩展了Box的值都实现了Boxed,但不能编译通过
}
}
英文:
This question is about the relation of an interface to the class which implements the interface. I can not see how this or this answers the question.
I created an interface Boxed
and an abstract generic class Box
, which implements the interface. Then I create two concrete classes IntegerBox
and StringBox
. Then I created a list of elements, which extend Box
with an IntegerBox
value and a StringBox
value. So far so good.
Now I want to assign List<? extends Box>
to List<Boxed>
. My expectation is, that this should be valid, because whatever extends Box
implements also Boxed
. But the compiler does not allow me. This is the error:
main.java:29: error: incompatible types: List<CAP#1> cannot be converted to List<Boxed>
List<Boxed> lb2 = laeb; // does not compile, although every value which extends Box implements Boxed
^
where CAP#1 is a fresh type-variable:
CAP#1 extends Box from capture of ? extends Box
1 error
I can copy the list:
List<? extends Box> laeb = List.of (new IntegerBox(42), new StringBox("answer"));
List<Boxed> lb1 = new ArrayList<> (laeb);
Every element of the type ? extends Box
is used to create a value if the type Boxed
. But the assignment reports an incompatible type. Why?
import java.util.List;
import java.util.ArrayList;
public class main
{
static interface Boxed { }
static abstract class Box<T> implements Boxed
{
T content;
Box (T content) { this.content = content; }
}
static class IntegerBox extends Box<Integer> { IntegerBox (Integer content) { super (content); } }
static class StringBox extends Box<String> { StringBox (String content) { super (content); } }
public static void main (String ...arguments) throws Exception
{
IntegerBox i = new IntegerBox(42);
StringBox s = new StringBox("answer");
List<? extends Box> laeb = List.of (i, s);
Boxed b0 = i; // => IntegerBox is compatible with Boxed
Boxed b1 = s; // => StringBox is compatible with Boxed
List<Boxed> lb1 = new ArrayList<> (laeb); // List<Boxed> can be created by values of "? extends Box"
List<Boxed> lb2 = laeb; // does not compile, although every value which extends Box implements Boxed
}
}
答案1
得分: 2
考虑这个例子:
List<StringBox> stringBoxList = new ArrayList<StringBox>();
List<? extends Box> boxList = stringBoxList; // 可以工作,StringBox 扩展自 Box
List<Boxed> boxedList = boxList; // 假设这个行得通
boxedList.add(new IntegerBox(42)); // 这行肯定能编译通过,它做了什么?
在最后一行之后,`stringBoxList` 会包含一个 `IntegerBox`,尽管最初它是一个 `ArrayList<StringBox>`。这是不好的。
这就是编译器所阻止的事情。
要绕过这个问题很简单:
```java
List<? extends Boxed> boxedList = boxList;
boxedList.add(new IntegerBox(42));
// 编译器禁止,因为 IntegerBox 不一定是与 boxedList 的元素“相同”的子类
或者,作为另一种选择,你可以写:
List<Boxed> boxedList = Collections.unmodifiableList(boxList);
// ...因为如果你不能修改列表,问题就不存在了。
(但坦率地说,https://stackoverflow.com/q/2745265/869736 讨论了这个问题,只是没有直接涉及到 ? extends
的情况。)
英文:
Consider this:
List<StringBox> stringBoxList = new ArrayList<StringBox>();
List<? extends Box> boxList = stringBoxList; // works, StringBox extends Box
List<Boxed> boxedList = boxList; // suppose this *did* work
boxedList.add(new IntegerBox(42)); // this line definitely compiles, what does it do?
After the final line, stringBoxList
would contain an IntegerBox
, despite originally being an ArrayList<StringBox>
. That's bad.
That's what the compiler is preventing.
To get around this is as simple as
List<? extends Boxed> boxedList = boxList;
boxedList.add(new IntegerBox(42));
// forbidden by the compiler, because IntegerBox is not necessarily the _same_ subclass
// of Boxed as boxedList's elements
Or, alternately, you could write
List<Boxed> boxedList = Collections.unmodifiableList(boxList);
...because the problem goes away if you can't modify the list.
(But, bluntly, https://stackoverflow.com/q/2745265/869736 covers this, just not the direct case of ? extends
.)
答案2
得分: 1
>现在我想将 List<? extends Box>
赋值给 List<Boxed>
。
你不能这样做,因为 List<Boxed>
不是上界通配符 <? extends Box>
的超类型。
将:
List<Boxed> lb2 = laeb;
改为:
List<? extends Boxed> lb2 = laeb;
>List<Boxed>
可以由“? extends Box
”的值创建
通过:
List<Boxed> lb1 = new ArrayList<>(laeb);
你实际上并没有用 <? extends Box>
类型创建列表,而是用上面你已经定义的不可变列表填充新列表 - List.of(i, s);
注意:
extends
具有语义,既类似于 Java 泛型中的extends
也类似于implements
。
>注意,在这个上下文中,extends 被用在一个通用的意义上,既表示类似于“extends”(如类)又表示类似于“implements”(如接口)。- 类型擦除在接口中同样起作用。参见 来自 docs.oracle 的这个引用:
>因此,生成的字节码仅包含普通类、接口和方法。
英文:
>Now I want to assign List<? extends Box>
to List<Boxed>
.
You cannot do that, as List<Boxed>
is not a supertype of your upper-bounded wildcard <? extends Box>
.
Change:
List<Boxed> lb2 = laeb;
with:
List<? extends Boxed> lb2 = laeb;
>List<Boxed>
can be created by values of "? extends Box
"
With:
List<Boxed> lb1 = new ArrayList<>(laeb);
you are not really creating the list with <? extends Box>
type, but you rather populate the new list, with already defined immutable list you create above - List.of(i, s);
Note, that:
extends
has the semantics of both -extends
andimplements
in Java Generics.
>Note that, in this context, extends is used in a general sense to mean either "extends" (as in classes) or "implements" (as in interfaces).- Type Erasure works for interfaces alike. See this quote from docs.oracle:
>The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论