将一系列实现了某个接口的泛型赋值给同一接口的另一个列表。

huangapple go评论83阅读模式
英文:

Assignment of a list of generics implementing an interface to a list of the same interface

问题

这个问题涉及接口与实现该接口的类之间的关系。我看不出这个链接这个链接是如何回答这个问题的。

我创建了一个名为Boxed的接口和一个实现该接口的抽象泛型类Box。然后我创建了两个具体的类IntegerBoxStringBox。接着,我创建了一个元素列表,这些元素扩展了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&lt;StringBox&gt; stringBoxList = new ArrayList&lt;StringBox&gt;();
List&lt;? extends Box&gt; boxList = stringBoxList; // works, StringBox extends Box

List&lt;Boxed&gt; 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&lt;StringBox&gt;. That's bad.

That's what the compiler is preventing.

To get around this is as simple as

List&lt;? extends Boxed&gt; boxedList = boxList;

boxedList.add(new IntegerBox(42));
// forbidden by the compiler, because IntegerBox is not necessarily the _same_ subclass    
// of Boxed as boxedList&#39;s elements

Or, alternately, you could write

List&lt;Boxed&gt; 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&lt;? extends Box&gt; 赋值给 List&lt;Boxed&gt;

你不能这样做,因为 List&lt;Boxed&gt; 不是上界通配符 &lt;? extends Box&gt; 的超类型。

将:

List&lt;Boxed&gt; lb2 = laeb;

改为:

List&lt;? extends Boxed&gt; lb2 = laeb;

>List&lt;Boxed&gt; 可以由“? extends Box”的值创建

通过:

List&lt;Boxed&gt; lb1 = new ArrayList&lt;&gt;(laeb);

你实际上并没有用 &lt;? extends Box&gt; 类型创建列表,而是用上面你已经定义的不可变列表填充新列表 - List.of(i, s);


注意:

  • extends 具有语义,既类似于 Java 泛型中的 extends 也类似于 implements
    >注意,在这个上下文中,extends 被用在一个通用的意义上,既表示类似于“extends”(如类)又表示类似于“implements”(如接口)。
  • 类型擦除在接口中同样起作用。参见 来自 docs.oracle 的这个引用
    >因此,生成的字节码仅包含普通类、接口和方法。
英文:

>Now I want to assign List&lt;? extends Box&gt; to List&lt;Boxed&gt;.

You cannot do that, as List&lt;Boxed&gt; is not a supertype of your upper-bounded wildcard &lt;? extends Box&gt;.

Change:

List&lt;Boxed&gt; lb2 = laeb;

with:

List&lt;? extends Boxed&gt; lb2 = laeb;

>List&lt;Boxed&gt; can be created by values of "? extends Box"

With:

List&lt;Boxed&gt; lb1 = new ArrayList&lt;&gt;(laeb);

you are not really creating the list with &lt;? extends Box&gt; 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 and implements 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.

huangapple
  • 本文由 发表于 2020年9月7日 01:24:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/63766971.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定