javagenerics超级关键字
我经历了这些话题
- generics..? 超级T
- 用“超级”关键字绑定generics
但是,我似乎仍然是super
关键词丢失:
-
当我们宣布这样一个集合:
List<? super Number> list = null; list.add(new Integer(0));//this compiles list.add(new Object());//this doesn't compile
不应该是相反的 – 我们有一个列表,其中包含一些对象(未知types),它们是
Number
父项。 所以Object
应该适合(因为它是数字的父亲),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... }
编写上面的代码是不可能的(我的理智认为这是正确的行为),但基本逻辑可能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代表生产者
extends
,super
消费者
- “PECS代表生产者
相关问题
- 太多的列表,PECS,
new Integer(0)
和valueOf
等等
对于第一部分List<Number>
适合List<? super Number>
List<? super Number>
但不能将Object
添加到List<Number>
。 这就是为什么你不能添加一个Object
List<? super Number>
List<? super Number>
。
另一方面,您可以将Number
( Number
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
的“超级层次”(即Number
和Object
):-
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
到列表中,你只需要处理它,如果你想把它们看作比Number
类Object
更普遍的东西 – 没关系,我保证它们至less是Number
,但是如果你愿意的话,可以更一般地对待它们。
而extends
说,“我不在乎你给我什么样的List
,只要每个元素至less是一个Number
,它可以是任何types的Number
,甚至你自己的奇怪的,定制的,编造的Number
只要他们实现了这个接口,我们就很好,我不会在你的列表中添加任何东西,因为我不知道你在那里使用了什么实际的具体types。