[英]Java generics super keyword
I went through these topics我经历了这些话题
However, I still seem to be kind of lost with super
keyword:但是,我似乎仍然对
super
关键字感到迷茫:
When we declare a collection like that:当我们像这样声明一个集合时:
List<? super Number> list = null; list.add(new Integer(0)); // this compiles list.add(new Object()); // this doesn't compile
shouldn't it be the opposite - we have a list that contains some objects (of unknown type) which are parents of Number
.不应该相反 - 我们有一个包含一些对象(未知类型)的列表,它们是
Number
父对象。 So Object
should fit (since it is the parent of Number
), and Integer
shouldn't.所以
Object
应该适合(因为它是Number
的父级),而Integer
不应该。 The opposite is the case for some reason.出于某种原因,情况正好相反。
Provided we have the following code如果我们有以下代码
static void test(List<? super Number> param) { param.add(new Integer(2)); } public static void main(String[] args) { List<String> sList = new ArrayList<String>(); test(sList); // will never compile, however... }
It is impossible to compile the above code (and my sanity suggests that this is the right behaviour), but the basic logic could prove the opposite:编译上面的代码是不可能的(我的理智表明这是正确的行为),但基本逻辑可以证明相反:
String is Object, Object is superclass of Number. So String should work.
I know this is crazy but isn't this the reason why they didn't allow <S super T>
constructs?我知道这很疯狂,但这不是他们不允许
<S super T>
构造的原因吗? If yes, then why <? super T>
如果是,那为什么
<? super T>
<? super T>
is allowed? <? super T>
允许吗?
Could someone help me restore the missing part of this logic chain?有人可以帮我恢复这个逻辑链的缺失部分吗?
The bounded wildcard in List<? super Number>
List<? super Number>
的有界通配符List<? super Number>
can capture Number
and any of its supertypes. List<? super Number>
可以捕获Number
及其任何超类型。 Since Number extends Object implements Serializable
, this means that the only types that are currently capture-convertible by List<? super Number>
由于
Number extends Object implements Serializable
,这意味着目前只有List<? super Number>
List<? super Number>
are: List<? super Number>
是:
List<Number>
List<Object>
List<Serializable>
Note that you can add(Integer.valueOf(0))
to any of the above types.请注意,您可以
add(Integer.valueOf(0))
到上述任何类型中。 however, you CAN'T add(new Object())
to a List<Number>
or a List<Serializable>
, since that violates the generic type safety rule.但是,您不能
add(new Object())
到List<Number>
或List<Serializable>
,因为这违反了通用类型安全规则。
Hence it is NOT true that you can add
any supertype of Number
to a List<? super Number>
因此,它是不正确的,你可以
add
任何超Number
到List<? super Number>
List<? super Number>
; List<? super Number>
; that's simply not how bounded wildcard and capture conversion work.这根本不是有界通配符和捕获转换的工作方式。 You don't declare a
List<? super Number>
你没有声明一个
List<? super Number>
List<? super Number>
because you may want to add an Object
to it (you can't!); List<? super Number>
因为你可能想给它添加一个Object
(你不能!); you do because you want to add Number
objects to it (ie it's a "consumer" of Number
), and simply a List<Number>
is too restrictive.你这样做是因为你想向它添加
Number
对象(即它是Number
的“消费者”),而简单的List<Number>
限制太多。
extends
, consumer- super
extends
,消费者super
new Integer(0)
vs valueOf
, etcnew Integer(0)
与valueOf
等For the first part List<Number>
fits in List<? super Number>
对于第一部分
List<Number>
适合List<? super Number>
List<? super Number>
but you can't add an Object
to a List<Number>
. List<? super Number>
但您不能将Object
添加到List<Number>
。 That's why you can't add an Object
to List<? super Number>
这就是为什么您不能将
Object
添加到List<? super Number>
List<? super Number>
. List<? super Number>
。
On the other hand you can add every subclass of Number
( Number
included) to your list.另一方面,您可以将
Number
每个子类(包括Number
)添加到您的列表中。
For the second part, String
is an Object
, but String
isn't a superclass of Number
.对于第二部分,
String
是一个Object
,但String
不是Number
的超类。
If it worked like this, as every class is a subclass of Object
, super
would have no meaning.如果它像这样工作,因为每个类都是
Object
的子类, super
将没有意义。
List<? super Number>
List<? super Number>
List<? super Number>
: List<? super Number>
:List<Object>
List<Object>
List<Object>
will work List<Object>
会起作用Object
fits in <? super Number>
Object
适合<? super Number>
<? super Number>
Number
to a List<Object>
Number
任何子类型添加到List<Object>
String
in it the only thing you're sure of is that you can add any subclass of Number
.String
,您唯一确定的是您可以添加Number
任何子类。List<Number>
:List<Number>
:
List<Number>
will work List<Number>
会起作用Number
fits in <? super Number>
Number
适合<? super Number>
<? super Number>
Number
to a List<Number>
Number
任何子类型添加到List<Number>
List<Integer>
(or any subclass of Number
):List<Integer>
(或Number
任何子类):
List<Integer>
won't work List<Integer>
不起作用Number
so it is exactly what we want to avoid Number
的子类,所以它正是我们想要避免的Integer
fits in a Number
you wouldn't be abble to add any subclass of Number
in a List<Integer>
(for example a Float
)Integer
适合Number
您也无法在List<Integer>
添加Number
任何子类(例如Float
)super
doesn't mean a subclass. super
并不意味着子类。List<String>
(or any class not extending Number
nor in the "super hierarchy" of Number
(ie. Number
and Object
) :List<String>
(或不延伸的任何类Number
,也没有在的“超级层次” Number
(即Number
和Object
):
List<String>
won't work List<String>
不起作用String
doesn't fit in Number
"super hierarchy" String
不适合Number
“超级层次结构”String
fits in Object
(which is a super class of Number
) you woudln't be sure to be able to add a Number
to a List
that contain any subclass from one of the super classes of Number
)String
适合Object
(它是Number
的超类),您也不能确保能够将Number
添加到包含Number
超类之一的任何子类的List
中)super
doesn't mean any subclass of one of the super classes, it only means one of the super classes. super
并不意味着超类之一的任何子类,它只意味着超类之一。 How does it work ?它是如何工作的?
You could say that as long as you can add any subclass of Number
with your typed List
, it respects the super
keyword.您可以说,只要您可以使用键入的
List
添加Number
任何子类,它就会尊重super
关键字。
I didn't get it for a while.我有一段时间没有得到它。 Many of the answers here, and the other questions show specifically when and where certain usages are errors, but not so much why.
此处的许多答案和其他问题具体显示了某些用法在何时何地出现错误,但并没有说明为什么。
This is how I finally got it.这就是我最终得到它的方式。 If I have a function that adds
Number
s to a List
, I might want to add them of type MySuperEfficientNumber
which is my own custom class that implements Number
(but is not a subclass of Integer
).如果我有一个将
Number
s 添加到List
的函数,我可能想添加MySuperEfficientNumber
类型的它们,这是我自己的自定义类,它实现了Number
(但不是Integer
的子类)。 Now the caller might not know anything about MySuperEfficientNumber
, but as long as they know to treat the elements added to the list as nothing more specific than Number
, they'll be fine.现在调用者可能对
MySuperEfficientNumber
,但只要他们知道将添加到列表中的元素视为仅比Number
更具体的元素,他们就可以了。
If I declared my method as:如果我将我的方法声明为:
public static void addNumbersToList(List<? extends Number> numbers)
Then the caller could pass in a List<Integer>
.然后调用者可以传入一个
List<Integer>
。 If my method added a MySuperEfficientNumber
to the end of numbers
, then the caller would no longer have a List
of Integer
s and the following code wouldn't work:如果我的方法添加一个
MySuperEfficientNumber
在2002年底numbers
,那么调用者将不再有一个List
的Integer
S和下面的代码是行不通的:
List<Integer> numbers = new ArrayList<Integer>();
addNumbersToList(numbers);
// The following would return a MySuperEfficientNumber not an Integer
Integer i = numbers.get(numbers.size()-1)
Obviously this can't work.显然这是行不通的。 And the error would be inside the
addNumbersToList
method.错误将在
addNumbersToList
方法内。 You'd get something like:你会得到类似的东西:
The method add... is not applicable for the arguments (MySuperEfficientNumber)
Because numbers
could be any specific kind of Number
, not necessarily something that MySuperEfficientNumber
is compatible with.因为
numbers
可以是任何特定类型的Number
,不一定与MySuperEfficientNumber
兼容。 If I flipped the declaration around to use super
, the method would compile without error, but the caller's code would fail with:如果我翻转声明以使用
super
,该方法将无错误地编译,但调用者的代码将失败:
The method addNumbersToList(List<? super Number>)... is not applicable for the arguments (List<Integer>)
Because my method is saying, "Don't think that your List
can be of anything more specific than Number
. I might add all sorts of weird Number
s to the list, you'll just have to deal with it. If you want to think of them as something even more general than Number
-- like Object
-- that's fine, I guarantee they'll be at least Number
s, but you can treat them more generally if you want."因为我的方法是说,“不要认为你的
List
可以比Number
更具体。我可能会在列表中添加各种奇怪的Number
,你只需要处理它。如果你想将它们视为比Number
更通用的东西——比如Object
那很好,我保证它们至少是Number
s,但如果你愿意,你可以更一般地对待它们。”
Whereas extends
is saying, "I don't really care what kind of List
you give me, as long as each element is at least a Number
. It can be any kind of Number
, even your own weird, custom, made-up Number
s. As long as they implement that interface, we're good. I'm not going to be adding anything to your list since I don't know what actual concrete type you're using there."而
extends
是说,“我真的不在乎你给我什么样的List
,只要每个元素至少是一个Number
。它可以是任何类型的Number
,甚至是你自己奇怪的、自定义的、虚构的Number
s. 只要他们实现了那个接口,我们就很好。我不会在你的列表中添加任何东西,因为我不知道你在那里使用的是什么实际的具体类型。”
List<? super Number>
List<? super Number>
means that the reference type of the variable suggests we have a list of Numbers, Objects or Serializables. List<? super Number>
意味着变量的引用类型表明我们有一个数字、对象或序列化的列表。
The reason you can't add an Object, is because the compiler does not know WHICH of these classes are in the generic definition of the actual instantiated object, so it only allows you to pass Number or subtypes of Number, like Double, Integer and so on.不能添加 Object 的原因是因为编译器不知道这些类中的哪些在实际实例化对象的泛型定义中,所以它只允许您传递 Number 或 Number 的子类型,如 Double、Integer 和等等。
Let's say we have a method that returns a List<? super Number>
假设我们有一个返回
List<? super Number>
List<? super Number>
. List<? super Number>
。 The creation of the object inside the method is encapsulated from our view, we just can't say if it is something like this:方法内部对象的创建是从我们的角度封装的,我们只是不能说它是不是这样的:
List<? super Number> returnValue = new LinkedList<Object>();
or或
List<? super Number> returnValue = new ArrayList<Number>();
So, the generic type could be Object or Number.因此,泛型类型可以是 Object 或 Number。 In both cases, we would be allowed to add Number, but only in one case we would be allowed to add Object.
在这两种情况下,我们都可以添加 Number,但只有在一种情况下我们才可以添加 Object。
You have to distinguish between the reference type and the actual object type in this situation.在这种情况下,您必须区分引用类型和实际对象类型。
List<? super Number>
List<? super Number>
is such a List<AncestorOfNumber>
where we can implicitely cast each Number
to its super type AncestorOfNumber
. List<? super Number>
就是这样一个List<AncestorOfNumber>
,我们可以隐式地将每个Number
为它的超类型AncestorOfNumber
。
Consider this: What generic type needs to be ????
考虑一下:需要什么泛型类型
????
in the following example?在下面的例子中?
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); }
The answer: ????
答案:
????
is anything to which we can cast BufferedInputStream
, which is that very same or one of its ancestors: ? super BufferedInputStream
我们可以将
BufferedInputStream
为任何东西,它非常相同或其祖先之一: ? super BufferedInputStream
? super BufferedInputStream
May I give a very simple Example.我可以举一个非常简单的例子。
public void add(List<? super Number> list) {
}
this will allow these calls这将允许这些调用
add(new LinkedList<Number>());
and everything above Number like和上面的所有数字像
add(new LinkedList<Object>());
but nothing below the hierarchy so not但没有低于层次结构所以不是
add(new LinkedList<Double>());
or或
add(new LinkedList<Integer>());
So since its not clear for the program to know whether you give a List with Number or Object the compiler cannot allow you to add anything above Number to it.因此,由于程序不清楚您给出的是带有数字的列表还是带有对象的列表,因此编译器不能允许您向其中添加数字以上的任何内容。
For example a List would not accept an Object in spite of Object who would accept a Number.例如,尽管对象会接受数字,但列表不会接受对象。 But since this is not clear the only valid input would be Number and its sub types.
但由于尚不清楚,唯一有效的输入是 Number 及其子类型。
There are two angles here: what you can put into a collection and what you can get from a collection, when bounded types are involved.这里有两个角度:当涉及有界类型时,您可以将什么放入集合中,以及您可以从集合中获得什么。
Let's look at the ? extends Number
让我们看看
? extends Number
? extends Number
case first.首先
? extends Number
案例。 When a collection with such bounds is defined, what we know is that : every element will have an upper bound as Number
.当定义了具有这样边界的集合时,我们知道:每个元素都有一个作为
Number
的上限。 We don't know the exact type (might be an Integer/Long/etc
), but we do know, for sure, that its upper bound is Number
.我们不知道确切的类型(可能是
Integer/Long/etc
),但我们确实知道,它的上限是Number
。
So reading from such a collection gets us a Number
.所以从这样的集合中读取我们得到了一个
Number
。 This is the only guaranteed type we can get from it.这是我们可以从中获得的唯一保证类型。
Writing to such a collection is prohibited.禁止写入此类集合。 But why?
但为什么? Didn't I say that while we read - we will always get a
Number
, so why prohibit writing to it?我不是说过在我们阅读时 - 我们总是会得到一个
Number
,那么为什么要禁止写入呢? The situation is slightly more involved here:这里的情况稍微复杂一点:
List<Integer> ints = ....;
List<? extends Number> numbers = ints;
numbers.add(12D); // add a double in here
If addition would have been allowed into numbers
, you could have effectively added a Double
in a List of Integers
.如果允许对
numbers
,则可以有效地在List of Integers
添加一个Double
。
Now to your example:现在以您的示例为例:
List<? super Number> list = null;
list.add(new Integer(0));
list.add(new Object());
We know about list
that it contains a certain supertype of Number
, for example Object
.我们知道
list
包含某个Number
超类型,例如Object
。
Reading from such a list would get us a certain type X
, where X
would be a parent of Number
.从这样的列表中读取将获得特定类型
X
,其中X
将是Number
的父级。 So what would that be?那会是什么? You can't really know.
你真的不可能知道。 It could be a theoretical
MyNumber extends Number
, or much simpler: an Object
.它可以是理论上的
MyNumber extends Number
,或者更简单: Object
。 Since you can't know for sure, the only safe thing to read from that would be the super-type of everything - Object
.由于您无法确定,因此唯一安全的读取方式是所有内容的超类型 -
Object
。
What is a bit weird may be :有点奇怪的可能是:
List<? super String> list = ...;
String s = list.get(0); // fails, compiler does not care that String is final
Writing to it is slightly more complicated, but only slightly.写入它稍微复杂一些,但只是稍微复杂一些。 Remember what we know is inside that
list
: it's a type that Number
extends/implements (if it were an interface), so you can always assign a subtype (or Number
itself) to that supertype .记住我们所知道的在该
list
:它是Number
扩展/实现的类型(如果它是一个接口),因此您始终可以为该超类型分配一个子类型(或Number
本身)。
Some type X
/ \
|
Number
/ \
|
Some type Y that we an put in here
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.