简体   繁体   English

Java泛型超级关键字

[英]Java generics super keyword

I went through these topics我经历了这些话题

However, I still seem to be kind of lost with super keyword:但是,我似乎仍然对super关键字感到迷茫:

  1. 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.出于某种原因,情况正好相反。

  1. 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任何超NumberList<? 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>限制太多。

References参考文献

See also另见

  • Effective Java 2nd Edition , Item 28: Use bounded wildcards to increase API flexibility Effective Java 2nd Edition ,Item 28:使用有界通配符来增加 API 的灵活性
    • "PECS stands for producer- extends , consumer- super “PECS 代表生产者extends ,消费者super

Related questions相关问题

  • Too many to list, PECS, new Integer(0) vs valueOf , etc太多无法列出,PECS, new 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将没有意义。


Let's see every possible cases with List<? super Number>让我们看看List<? super Number> List<? super Number> : List<? super Number> :


  • The passed list is a List<Object>传递的列表是一个List<Object>
    • List<Object> will work List<Object>会起作用
    • Object fits in <? super Number> Object适合<? super Number> <? super Number>
    • You can add any subtype of Number to a List<Object>您可以将Number任何子类型添加到List<Object>
    • Even if you could also add String in it the only thing you're sure of is that you can add any subclass of Number .即使您也可以在其中添加String ,您唯一确定的是您可以添加Number任何子类。

  • The passed list is a List<Number> :传递的列表是一个List<Number>
    • List<Number> will work List<Number>会起作用
    • Number fits in <? super Number> Number适合<? super Number> <? super Number>
    • You can add any subtype of Number to a List<Number>您可以将Number任何子类型添加到List<Number>

  • The passed list is a List<Integer> (or any subclass of Number ):传递的列表是List<Integer> (或Number任何子类):
    • List<Integer> won't work List<Integer>不起作用
    • Integer is a subclass of Number so it is exactly what we want to avoid Integer 是Number的子类,所以它正是我们想要避免的
    • Even if an 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并不意味着子类。

  • The passed list is a List<String> (or any class not extending Number nor in the "super hierarchy" of Number (ie. Number and Object ) :传递的列表是一个List<String> (或不延伸的任何类Number ,也没有在的“超级层次” Number (即NumberObject ):
    • List<String> won't work List<String>不起作用
    • String doesn't fit in Number "super hierarchy" String不适合Number “超级层次结构”
    • Even if 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 ,那么调用者将不再有一个ListInteger 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM