javagenerics超级关键字

我经历了这些话题

  • generics..? 超级T
  • 用“超级”关键字绑定generics

但是,我似乎仍然是super关键词丢失:

  1. 当我们宣布这样一个集合:

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

    不应该是相反的 – 我们有一个列表,其中包含一些对象(未知types),它们是Number父项。 所以Object应该适合(因为它是数字的父亲), Integer不应该。 出于某种原因,情况正好相反。

  2. 只要我们有以下代码

     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... } 

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

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

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

有人能帮助我恢复这个逻辑链中缺less的部分吗?

List<? super Number>的有界通配符 List<? super Number>可以捕获Number及其任何超types。 由于Number extends Object implements Serializable ,这意味着唯一的types,目前捕获可转换的List<? super Number> List<? super Number>是:

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

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

因此,你可以add任何超types的Number到一个List<? super Number> List<? super Number> ; 这根本不是多么有界的通配符和捕捉转换工作。 你不要声明一个List<? super Number> List<? super Number>因为你可能想添加一个Object (你不能!); 你这样做是因为你想添加Number对象(即它是Number的“消费者”),而List<Number>太简单了。

参考

  • Angelika Langer的generics常见问题
    • 什么是有界通配符?
    • 什么时候使用带有下限的通配符参数化types? (“当一个具体的参数化types太严格了。”)
    • 为什么没有types参数的下限? (“因为它没有意义。”)
  • JLS 5.1.10捕捉转换

也可以看看

  • Effective Java第2版 ,第28项:使用有界通配符来增加API的灵活性
    • “PECS代表生产者extendssuper消费者

相关问题

  • 太多的列表,PECS, new Integer(0)valueOf等等

对于第一部分List<Number>适合List<? super Number> List<? super Number>但不能将Object添加到List<Number> 。 这就是为什么你不能添加一个Object List<? super Number> List<? super Number>

另一方面,您可以将NumberNumber included)的每个子类添加到列表中。

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

如果这样工作,因为每个类都是Object的子类, super就没有意义了。


让我们看看List<? super Number>每个可能的情况 List<? super Number>


  • 传递的列表是一个List<Object>
    • List<Object>将工作
    • Object适合<? super Number> <? super Number>
    • 您可以将任何数字的子types添加到List<Object>
    • 即使你也可以在其中添加String ,唯一可以确定的是你可以添加Number任何子类。

  • 传递的列表是一个List<Number>
    • List<Number>将工作
    • Number适合<? super Number> <? super Number>
    • 您可以将任何数字的子types添加到List<Number>

  • 传递的列表是List<Integer> (或Number任何子类):
    • List<Integer>将不起作用
    • 整数是Number一个子类,所以它正是我们想要避免的
    • 即使一个Integer适合一个Number你也不能在一个List<Integer> (例如一个Float )中添加任何Number子类,
    • super并不意味着一个子类。

  • 传递的列表是一个List<String> (或者任何没有扩展Number类,也不是Number的“超级层次”(即NumberObject ):
    • List<String>不起作用
    • String不适合Number “超级层次”
    • 即使String适合Object (它是Number一个超类),您也不一定能够将Number添加到包含来自Number的超类之一的任何子类的List
    • super并不意味着其中一个超类的任何子类,它只意味着超类中的一个。

它是如何工作的 ?

你可以说,只要你可以添加任何types的List子类,它尊重super关键字。

我有同样的问题。 我认为语法是混乱的原因。

List<? super Number> List<? super Number>似乎提示“ 超级types的数字列表 ”,但它实际上意味着什么是“ 列表中的数字作为超types的东西 ”。

一旦我开始阅读,就像这样点击。

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

你不能添加一个Object的原因是因为编译器不知道这些类中的WHICH是否在实际实例化对象的通用定义中,所以它只允许你传递Number或者Number的子types,比如Double,Integer和等等。

比方说,我们有一个方法返回一个List<? super Number> List<? super Number> 。 方法内部的对象的创build是从我们的视图封装的,我们不能说是否是这样的:

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

要么

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

所以,genericstypes可以是Object或Number。 在这两种情况下,我们都可以添加Number,但只有在一种情况下,我们才可以添加Object。

在这种情况下,你必须区分引用types和实际的对象types。

List<? super Number> List<? super Number>就是这样一个List<AncestorOfNumber> ,我们可以隐式地将每个Number为它的超typesAncestorOfNumber

考虑这个:什么genericstypes需要???? 在下面的例子中?

 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 ? super BufferedInputStream

我没有得到它一段时间。 这里的许多答案和其他问题具体显示何时何地某些用法是错误的,但不是那么多的原因。

这是我终于明白的。 如果我有一个函数将Number添加到List ,我可能想要添加他们的typesMySuperEfficientNumber这是我自己的自定义类实现Number (但不是Integer的子类)。 现在调用者可能不知道任何有关MySuperEfficientNumber ,但只要他们知道将添加到列表中的元素视为没有比Number更具体的特性,他们就可以。

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

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

然后调用者可以传递一个List<Integer> 。 如果我的方法在numbers的末尾添加了MySuperEfficientNumber ,则调用者将不再具有Integer List ,下面的代码将不起作用:

 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可以是任何特定types的Number ,不一定是MySuperEfficientNumber兼容的东西。 如果我翻转声明来使用super ,该方法将编译没有错误,但调用者的代码将失败:

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

因为我的方法是说:“不要以为你的List可以是任何比Number更具体的东西,我可能会添加各种奇怪的Number到列表中,你只需要处理它,如果你想把它们看作比NumberObject更普遍的东西 – 没关系,我保证它们至less是Number ,但是如果你愿意的话,可以更一般地对待它们。

extends说,“我不在乎你给我什么样的List ,只要每个元素至less是一个Number ,它可以是任何types的Number ,甚至你自己的奇怪的,定制的,编造的Number只要他们实现了这个接口,我们就很好,我不会在你的列表中添加任何东西,因为我不知道你在那里使用了什么实际的具体types。