java泛型超级关键字

时间:2010-10-02 19:15:04

标签: java generics

我经历了这些话题

但是,我似乎仍然对super关键字感到失望:

  1. 当我们宣布这样的集合时:

    List<? super Number> list = null;
    list.add(new Integer(0));//this compiles
    list.add(new Object());//this doesn't compile
    

    不应该相反 - 我们有一个列表,其中包含一些Number父项的对象(未知类型)。因此,Object应该适合(因为它是Number的父级),Integer不应该。static void test(List<? super Number> param) { param.add(new Integer(2)); } public static void main(String[] args) { ArrayList<String> sList = new ArrayList<String>(); test(sList); //will never compile, however... } 。情况恰恰相反。

  2. 如果我们有以下代码

    String is Object, Object is superclass of Number. So String should work.

    编译上面的代码是不可能的(我的理智表明这是正确的行为),但基本逻辑可能证明相反:

    <S super T>

    我知道这很疯狂,但这不是他们不允许<? super T>构造的原因吗?如果是,那么为什么允许{{1}}?

  3. 有人可以帮我恢复这条逻辑链的缺失部分吗?

7 个答案:

答案 0 :(得分:92)

List<? super Number>中的有界通配符可以捕获Number及其任何超类型。自Number extends Object implements Serializable起,这意味着List<? super Number>目前只能抓取可转换的类型是:

  • List<Number>
  • List<Object>
  • List<Serializable>

请注意,您可以add(Integer.valueOf(0))使用上述任何类型。但是,您不能 add(new Object())List<Number>List<Serializable>,因为这违反了通用类型安全规则。

因此, NOT 是真的,您可add NumberList<? super Number>的任何超类型;这根本不是限制通配符和捕获转换的工作方式。您没有声明List<? super Number>,因为您可能想要向其添加Object(您不能!);你这样做是因为你想向它添加Number个对象(即它是Number的“消费者”),而List<Number>仅限于extends

参考

另见

  • Effective Java 2nd Edition,第28项:使用有界通配符来提高API灵活性
    • “PECS代表制作人 - super,消费者 - new Integer(0)

相关问题

  • 列出太多,PECS,valueOf vs {{1}}等

答案 1 :(得分:20)

第一部分List<Number>适合List<? super Number>,但您无法将Object添加到List<Number>。这就是为什么您无法将Object添加到List<? super Number>

另一方面,您可以将Number(包括Number)的每个子类添加到列表中。

对于第二部分,StringObject,但String不是Number的超类。

如果它像这样工作,因为每个类都是Object的子类,super没有任何意义。


让我们看一下List<? super Number>的所有可能案例:


  • 传递的列表是 List<Object>
    • List<Object>可以使用
    • Object符合<? super Number>
    • 您可以将Number的任何子类型添加到List<Object>
    • 即使您还可以在其中添加String,您唯一可以确定的是可以添加Number的任何子类。

  • 传递的列表是 List<Number>
    • List<Number>可以使用
    • Number符合<? super Number>
    • 您可以将Number的任何子类型添加到List<Number>

  • 传递的列表是 List<Integer> (或Number的任何子类):
    • List<Integer>无效
    • Integer是Number的子类,因此它正是我们想要避免的内容
    • 即使Integer适合Number,您也无法在Number中添加List<Integer>的任何子类(例如Float })
    • super并不代表子类。

  • 传递的列表是 List<String> (或任何未扩展Number的类,也不在Number的“超级层次结构”中(即{{1} }和Number):
    • Object无效
    • List<String>不适合String“超级层次结构”
    • 即使Number符合StringObject的超类),您也无法确定能够Number添加Number {1}}包含来自List
    • 之一的超类的任何子类
    • Number并不意味着其中一个超类的任何子类,它只表示其中一个超类。

它是如何运作的?

您可以说,只要您可以使用键入的super添加Number的任何子类,它就会尊重List关键字。

答案 2 :(得分:4)

我有一段时间没有得到它。这里的许多答案以及其他问题都具体说明了某些用法何时何地出错,而不是原因。

这就是我最终得到它的方式。如果我有一个将Number添加到List的函数,我可能想要添加MySuperEfficientNumber类型的函数,这是我自己的实现Number的自定义类(但是不是Integer的子类。现在调用者可能对MySuperEfficientNumber一无所知,但只要他们知道将添加到列表中的元素视为比Number更具体,他们就可以了。

如果我将我的方法声明为:

public static void addNumbersToList(List<? extends Number> numbers)

然后来电者可以传入List<Integer>。如果我的方法在MySuperEfficientNumber的末尾添加了numbers,则调用者将不再拥有List Integer个,并且以下代码将无法正常工作:

List<Integer> numbers = new ArrayList<Integer>();
addNumbersToList(numbers);

// The following would return a MySuperEfficientNumber not an Integer
Integer i = numbers.get(numbers.size()-1)

显然这不起作用。错误将在addNumbersToList方法内。你会得到类似的东西:

The method add... is not applicable for the arguments (MySuperEfficientNumber)

因为numbers可以是任何特定类型的Number,但不一定是MySuperEfficientNumber兼容的内容。如果我将声明翻转为使用super,则该方法将编译时没有错误,但调用者的代码将失败:

The method addNumbersToList(List<? super Number>)... is not applicable for the arguments (List<Integer>)

因为我的方法是说,&#34;不要认为你的List可能比Number更具体。我可能会在列表中添加各种奇怪的Number,您只需处理它。如果您想将它们视为比Number更为通用的内容 - 例如Object - 那很好,我保证它们至少会Number s,但如果你愿意,你可以更普遍地对待它们。&#34;

extends正在说,&#34;我并不关心你给我的List是什么,只要每个元素至少是Number 。它可以是任何类型的Number,甚至是您自己的奇怪,自定义,组成Number。只要他们实现了这个界面,我们就会很好。我不会在您的列表中添加任何内容,因为我不知道您在那里使用的实际具体类型。&#34;

答案 3 :(得分:3)

List<? super Number>表示变量的引用类型表明我们有一个Numbers,Objects或Serializables列表。

您无法添加Object的原因是因为编译器不知道这些类的WHICH在实际实例化对象的泛型定义中,因此它只允许您传递Number的数字或子类型,像Double,Integer等。

假设我们有一个返回List<? super Number>的方法。在方法中创建对象是从我们的视图中封装的,我们只是不能说它是否是这样的:

List<? super Number> returnValue = new LinkedList<Object>();

List<? super Number> returnValue = new ArrayList<Number>();

因此,泛型类型可以是Object或Number。在这两种情况下,我们都可以添加Number,但只有在一种情况下我们才允许添加Object。

在这种情况下,您必须区分引用类型和实际对象类型。

答案 4 :(得分:2)

List<? super Number>就是List<AncestorOfNumber>,我们可以将每个Number隐含地投射到其超级类型AncestorOfNumber

请考虑以下示例:以下示例中需要哪种泛型类型????

InputStream mystream = ...;

void addTo(List<????> lsb) {
    lsb.add(new BufferedInputStream(mystream));
}

List<BufferedInputStream> lb = new ArrayList<>();
List<InputStream> li = new ArrayList<>();
List<Object> lo = new ArrayList<>();

...
{ addTo(lb); addTo(li); addTo(lo); }

答案:????是我们可以投射BufferedInputStream的任何东西,它是与祖先相同或相同的一个:? super BufferedInputStream

答案 5 :(得分:1)

我可以举一个非常简单的例子。

public void add(List<? super Number> list) {
}

将允许这些呼叫

add(new LinkedList<Number>());

以及以上所有内容类似

add(new LinkedList<Object>());

但没有层次结构下的任何内容,因此

add(new LinkedList<Double>());

add(new LinkedList<Integer>());

因此,由于尚不清楚程序是否知道您给出带有Number或Object的列表,因此编译器无法允许您在Number之上添加任何内容。

例如,列表将不接受对象,尽管对象将接受数字。但是由于不清楚,唯一有效的输入将是Number及其子类型。

答案 6 :(得分:1)

这里有两个角度:涉及边界类型时,您可以放入什么以及可以从集合中获得什么。


首先让我们看一下? extends Number的情况。当定义了具有这样边界的集合时,我们知道的是:每个元素的上限都为Number。我们不知道确切的类型(可能是Integer/Long/etc),但是我们确实知道它的上限是Number

因此,从这样的集合中阅读会给我们一个Number。这是我们可以从中获得的唯一保证类型。

禁止向这样的收藏写东西。但为什么?我不是在阅读的时候说过-我们总是会得到Number,所以为什么禁止写呢?这里涉及的情况稍微多一些:

 List<Integer> ints = ....;
 List<? extends Number> numbers = ints;
 numbers.add(12D); // add a double in here

如果允许在numbers中添加,则可以在Double中有效地添加List of Integers


现在来看您的示例:

 List<? super Number> list = null;
 list.add(new Integer(0));
 list.add(new Object());

我们知道list,其中包含Number的某个超类型,例如Object

从这样的列表中读取

可以为我们提供某种类型的X,其中XNumber的父级。那会是什么呢?你真的不知道它可以是理论上的MyNumber extends Number,也可以简单得多:Object。由于您不能确定,因此唯一可以读取的安全信息是所有内容的超类型-Object

有点奇怪的是:

List<? super String> list = ...;
String s = list.get(0); // fails, compiler does not care that String is final

写作稍微复杂一点,但只是一点点。记住我们知道的list内部内容:Number扩展/实现(如果是接口)的类型,因此您始终可以分配子类型(或Number本身)作为该超型

             Some type X
                 / \
                  |
                Number
                 / \
                  |
    Some type Y that we an put in here