【问题标题】:Difference for <? super/extends String> in method and variable declaration<? super/extends String> 在方法和变量声明中
【发布时间】:2014-09-11 18:16:14
【问题描述】:

给定:

import java.util.*;

public class Hancock {
    //insert code here
        list.add("foo");
    }
}

在第 5 行独立插入的哪两个代码片段在编译时不会出现警告? (选择两项)

A. public void addString(List list) {
B. public void addString(List<String> list) {
C. public void addString(List<? super String> list) {
D. public void addString(List<? extends String> list) {

正确答案是 B 和 C。

答案 A 和 B 对我来说很清楚。对于 C 和 D 的答案,我知道继承的方向,但是我不明白为什么答案 D 不能在 Eclipse 中编译,而所有其他答案都可以(A 警告泛型,B 和 C 没有警告)。

Eclipse 中答案 D 的错误是 The method add(capture#1-of ? extends String) in the type List&lt;capture#1-of ? extends String&gt; is not applicable for the arguments (String)

另一方面,它编译:

public void addString() {
    List<? extends String> list1 = new ArrayList<String>();
    List<? super String> list2 = new ArrayList<String>();
}

为什么?为什么&lt;? super String&gt; 在方法声明中不编译,而在变量声明中编译。

我知道String 是最终类,不能被任何其他类扩展,但这并不能向我解释这里发生了什么。

【问题讨论】:

标签: java generics collections extends super


【解决方案1】:

首先,泛型并不关心String 是最终的。它们对最终类和非最终类的工作方式相同。

考虑到这一点,为什么不允许 D 应该是显而易见的 - 如果是,您可以这样做:

void test() {
    List<Integer> integers = new ArrayList<Integer>();
    addADouble(integers);
    int a = integers.get(0); // ????
}

void addADouble(List<? extends Number> list) {
    list.add(new Double(5.0));
}

List&lt;? extends Number&gt; 是一个“扩展 Number 的东西的列表,但你不知道它到底是什么的列表”。 - 它可能是List&lt;Double&gt;,或List&lt;Integer&gt;,或List&lt;Number&gt;,或List&lt;YourCustomSubclassOfNumber&gt;,所以你不能添加任何东西,因为你不知道它是否是正确的类型。

【讨论】:

  • 现在我可能会更复杂一点;)System.out.println(new Double(5.0) instanceof Number); 返回true,那么为什么list.add(new Double(5.0)); 没有在您的代码中编译并出现我在问题中描述的类似错误? ? in &lt;? extends Number&gt; 是否代表从上面打印返回 true 的东西?
  • @Dave 我想我的解释很糟糕。您不能将Double 添加到List&lt;I don't know what goes here except that it extends Number&gt;。用一个不同的例子:你不能这样做List&lt;? extends Object&gt; list = new ArrayList&lt;String&gt;(); list.add(new Double(5.0));
【解决方案2】:

首先,让我们看看答案C:

public void addString(List<? super String> list) {
    list.add("foo");
}

此方法声明表示您将被允许传递 List 对象,这些对象由 String 的某个超类参数化,例如 StringObject。所以:

  1. 如果您通过List&lt;String&gt;list.add("foo") 将完全有效。
  2. 如果您通过List&lt;Object&gt;list.add("foo") 将完全有效,因为“foo”是String(您可以将String 添加到List&lt;Object&gt;)。

这意味着答案 C 是正确的。


现在让我们看看答案 D。

如果你有这样的方法声明:

public void addString(List<? extends String> list) {

}

这意味着您将能够传递由String一些未知 子类型参数化的List 对象。因此,当您执行 list.add("foo"); 时,编译器不会知道提供的对象是否具有与 Stringunknown 子类型匹配的类型,因此会引发编译时错误。


当你有:

public void addString() {
    List<? extends String> list1 = new ArrayList<String>();
    List<? super String> list2 = new ArrayList<String>();
}

这个片段编译得很好,因为list1 被定义为包含List 对象,这些对象属于String 的某些未知子类型,包括String 本身,这就是它有效的原因。 问题是您将无法添加任何内容null 除外。

对于list2,该变量可以保存List 对象,这些对象由String 的某些超类型参数化,包括String 本身。


更多信息:

【讨论】:

  • 其实你对“C”的解释是错误的。您不能Object 添加到list&lt;? super String&gt;。列表可能在运行时是List&lt;String&gt;,所以这将失败。
  • 我不同意。尝试List&lt;? super String&gt; list = new ArrayList&lt;Object&gt;(); 并将其传递给addString 方法。
  • 您的示例添加了string(这是您可以添加的唯一有效内容)。如果你尝试添加一个对象,你会得到:no suitable method found for add(Object) myList.add(new Object());
  • 仔细阅读我写的内容并告诉我在哪里可以添加Object 实例?我说了可以传List&lt;Object&gt;,不是说可以加Objects :)
  • 触摸。你是如何从修订历史中删除关于“添加”的评论的:P
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-23
  • 1970-01-01
相关资源
最近更新 更多