简体   繁体   English

Java泛型(有界通配符)

[英]Java Generics (bounded wildcards)

According to the book "Effective Java" of Joshua Bloch there is a rule about how/when use the bounded wildcards in generics. 根据Joshua Bloch的“Effective Java”一书,有一条关于如何/何时在泛型中使用有界通配符的规则。 This rule is PECS (Producer-Extends, Comsumer-Super). 此规则是PECS(Producer-Extends,Comsumer-Super)。 When I study the following example: 当我研究以下示例时:

Stack<Number> numberStack = new Stack<Number>();
Iterable<Integer> integers = ... ;
numberStack.pushAll(integers);

I understand that this rule fits perfect in this example. 我知道这个规则在这个例子中非常完美。 I have to declare the method pushAll as the following sample: 我必须将方法pushAll声明为以下示例:

// Wildcard type for parameter that serves as an E producer
public void pushAll(Iterable<? extends E> src) {
    for (E e : src)
    {
       push(e);
    }  
}

But what happens if I have the following example? 但是,如果我有以下示例会发生什么?

Stack<Integer> integerStack = new Stack<Integer>();
Iterable<Number> numbers = ... ;
integerStack.pushAll(numbers);

I have to declare the pushAll as it follows: 我必须声明pushAll如下:

public void pushAll(Iterable<? super E> src) {
    for (E e : src)
    {  
        push(e);
    }
}

According to PECS rule the above declaration is wrong. 根据PECS规则,上述声明是错误的。 But I want to have a Stack of Integer s and pass to this Stack a Number . 但是我希望有一个Stack of Integer并传递给Stack这个Number Why not to do it? 为什么不这样做?
Why should I always use the extends keyword? 为什么我总是要使用extends关键字? Why using super is wrong? 为什么使用super是错的?
Of course the same stands for the comsumer 's point of view. 当然,这同样代表了消费者的观点。 Why a consumer should always be super ? 为什么消费者应该永远super


PS: To be more specific you can find this above example at the sector " Item 28 " of the referred book. PS:更具体地说,你可以在推荐书的“ 第28项 ”部分找到上面的例子。

When you declare a Stack<Foo> you mean a Stack of Foos, or subclasses of Foo. 当你声明一个Stack<Foo>你指的是一堆Foo,或Foo的子类。 As an example, you would expect to be able to put a String in a Stack<Object> . 例如,您希望能够将String放入Stack<Object> The other way is not true, you should not be able to insert another Object, in a Stack<String> . 另一种方法不是这样,你不应该在Stack<String>插入另一个Object。

In your example you declare a Stack<Integer> . 在您的示例中,您声明Stack<Integer> You should be able to put Integers in this stack, but not other Numbers (like a Double), which you would if you declared the parameter <? super E> 你应该能够将Integers放在这个堆栈中,而不是其他的数字(如Double),如果你声明了参数<? super E> <? super E> . <? super E> That's why the put-method should have a paramter of the type <? extends E> 这就是为什么put-method应该有一个类型<? extends E>的参数<? extends E> <? extends E> . <? extends E>

Trying to store arbitrary numbers in a Stack can't possibly work, since a Number could be something other that an Integer. 尝试在堆栈中存储任意数字是不可能的,因为数字可能是整数的其他东西。 So your example doesn't make much sense. 所以你的例子没有多大意义。

You would use super when the object asts as a consumer, ie when instances of the generic type of the object are passed as arguments to methods of the object. 当对象作为使用者时,您将使用super,即当对象的泛型类型的实例作为参数传递给对象的方法时。 For example: 例如:

 Collections.sort(List<T>, Comparator<? super T>)

In this example, the sort method takes T instances from the collection, and passes them as argument to the compare(T o1, T o2) of the comparator. 在此示例中,sort方法从集合中获取T实例,并将它们作为参数传递给compare(T o1, T o2)

Contrast this to your first example, where the Iterable src is a producer. 将此与您的第一个示例进行对比,其中Iterable src是生产者。 The pushAll() method calls a method of the Iterable which roduces (ie returns) instances of T. In this case, the iterable is a producer, hence the use of ? extends T pushAll()方法调用Iterable的一个方法来生成(即返回)T的实例。在这种情况下,iterable是一个生产者,因此使用? extends T ? extends T

In the pushAll method, you are not passing type E , but any type that extends E . pushAll方法,你是不是传递一个类型E ,但延伸的任何类型E So, instead of passing an Iterable of Number s, you can pass any Iterable of a type that extends Number . 因此,而不是通过一个IterableNumber S,你可以通过任何Iterable扩展类型的Number

The original example uses a Number type because you can then pass any type that is a subclass of Number , like Integer , BigDecimal and so on. 原始示例使用Number类型,因为您可以传递任何类型为Number的子类,如IntegerBigDecimal等。

In your example, you are doing it the other way around. 在您的示例中,您正在以相反的方式执行此操作。 You are using Integer to declare your Stack . 您正在使用Integer声明您的Stack Therefore, pushAll will only be able to accept those classes that are extended by Integer . 因此, pushAll只能接受由Integer扩展的那些类。 You will not be able to use Numbers (or any other class, because Integer is a final class). 您将无法使用Numbers (或任何其他类,因为Integer是最终类)。

First thing to notice is that Integer extends Number, so you shouldn't be pushing Number objects into a Stack of Integers. 首先要注意的是Integer扩展了Number,所以你不应该将Number对象推入Stack of Integers。 However, the first sample will work with Integers, Floats, BigDecimal and all other Number subclasses. 但是,第一个示例将使用Integers,Floats,BigDecimal和所有其他Number子类。

Your example doesn't make much sense. 你的例子没有多大意义。 A construct like <? extends Number> 一个类似<? extends Number> <? extends Number> means that Number and every type is allowed which inheits from Number. <? extends Number>表示Number和每个类型都允许来自Number的inheits。 So you define an upper and a lower boundary, from type Number down to the most specific one. 因此,您可以定义上边界和下边界,从数字类型向下到最具体的边界。 The other way round, <? super Number> 反过来说, <? super Number> <? super Number> means that Number and any of its supertyes are allowed. <? super Number>表示允许Number及其任何超类。 Since Number extends Object and implements Serializable the following three types are allowed: 由于Number扩展了Object并实现了Serializable,因此允许以下三种类型:

  1. java.lang.Number java.lang.Number中
  2. java.lang.Object java.lang.Object继承
  3. java.io.Serializable 的java.io.Serializable

In your example you declare the generic type Stack<Integer> . 在您的示例中,您声明泛型类型Stack<Integer> Let's consider the following. 让我们考虑以下几点。

  1. Your Stack is never be able to hold items of any super type of Integer 您的堆栈永远不能保存任何超类型整数的项目
  2. Your Stack is never be able to hold items of any subtype of Integer, since Integer class is final and thus it can't be subclassed. 您的堆栈永远不能保存任何Integer 子类型的项目,因为Integer类是最终的,因此它不能被子类化。

So, if you want to declare the generic type Stack<Integer> , your iterable is of type Iterable<Integer> and thus your Stack can only hold items of type Integer. 因此,如果要声明泛型类型Stack<Integer> ,则您的iterable的类型为Iterable<Integer> ,因此您的Stack 只能保存Integer类型的项。 You are totally right with the mnemonic PECS , but this only works if you have choosen a concrete type which has at least one super type and at least one subtype. 你完全正确使用助记符PECS ,但这只有在你选择了至少有一个超类型和至少一个子类型的具体类型时才有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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