简体   繁体   中英

Java Generics (Wildcards)

I have a couple of questions about generic wildcards in Java:

  1. What is the difference between List<? extends T> List<? extends T> and List<? super T> List<? super T> ?

  2. What is a bounded wildcard and what is an unbounded wildcard?

In your first question, <? extends T> <? extends T> and <? super T> <? super T> are examples of bounded wildcards. An unbounded wildcard looks like <?> , and basically means <? extends Object> <? extends Object> . It loosely means the generic can be any type. A bounded wildcard ( <? extends T> or <? super T> ) places a restriction on the type by saying that it either has to extend a specific type ( <? extends T> is known as an upper bound), or has to be an ancestor of a specific type ( <? super T> is known as a lower bound).

The Java Tutorials have some pretty good explanations of generics in the articles Wildcards and More Fun with Wildcards .

If you have a class hierarchy A, B is a subclass of A, and C and D both are subclass of B like below

class A {}
class B extends A {}
class C extends B {}
class D extends B {}

Then

List<? extends A> la;
la = new ArrayList<B>();
la = new ArrayList<C>();
la = new ArrayList<D>();

List<? super B> lb;
lb = new ArrayList<A>(); //fine
lb = new ArrayList<C>(); //will not compile

public void someMethod(List<? extends B> lb) {
    B b = lb.get(0); // is fine
    lb.add(new C()); //will not compile as we do not know the type of the list, only that it is bounded above by B
}

public void otherMethod(List<? super B> lb) {
    B b = lb.get(0); // will not compile as we do not know whether the list is of type B, it may be a List<A> and only contain instances of A
    lb.add(new B()); // is fine, as we know that it will be a super type of A 
}

A bounded wildcard is like ? extends B ? extends B where B is some type. That is, the type is unknown but a "bound" can be placed on it. In this case, it is bounded by some class, which is a subclass of B.

Josh Bloch also has a good explanation of when to use super and extends in this google io video talk where he mentions the Producer extends Consumer super mnemonic.

From the presentation slides:

Suppose you want to add bulk methods to Stack<E>

void pushAll(Collection<? extends E> src);

– src is an E producer

void popAll(Collection<? super E> dst);

– dst is an E consumer

There may be times when you'll want to restrict the kinds of types that are allowed to be passed to a type parameter. For example, a method that operates on numbers might only want to accept instances of Number or its subclasses. This is what bounded type parameters are for.

Collection<? extends MyObject> 

means that it can accept all object who have IS- A relationship with MyObject (ie any object which is a type of myObject or we can say any object of any subclass of MyObject) or a object of MyObject class.

For example:

class MyObject {}

class YourObject extends MyObject{}

class OurObject extends MyObject{}

Then,

Collection<? extends MyObject> myObject; 

will accept only MyObject or children of MyObject(ie any object of type OurObject or YourObject or MyObject, but not any object of superclass of MyObject).

In general,

If a structure contains elements with a type of the form ? extends E ? extends E , we can get elements out of the structure, but we cannot put elements into the structure

List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3.14); // compile-time error
assert ints.toString().equals("[1, 2, 3.14]"); 

To put elements into the structure we need another kind of wildcard called Wildcards with super ,

 List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
    List<Integer> ints = Arrays.asList(5, 6);
    Collections.copy(objs, ints);
    assert objs.toString().equals("[5, 6, four]");

    public static <T> void copy(List<? super T> dst, List<? extends T> src) {
          for (int i = 0; i < src.size(); i++) {
                dst.set(i, src.get(i));
         }
    }

Generic wildcards are created to make methods that operate on Collection more reusable.

For example, if a method has a parameter List<A> , we can only give List<A> to this method. It is a waste for this method's funtion under some circumstances:

  1. If this method only reads objects from List<A> , then we should be allowed to give List<A-sub> to this method. (Because A-sub IS a A)
  2. If this method only inserts objects to List<A> , then we should be allowed to give List<A-super> to this method. (Because A IS a A-super)

Pre Requirements

public class A { }
public class B extends A { }
public class C extends A { }

List<A> listA = new ArrayList<A>();
List<B> listB = new ArrayList<B>();

The problem

listB = listA; //not compiled
listA = listB; //not compiled

listB = listA; In listA you can insert objects that are either instances of A, or subclasses of A (B and C). When you make listB = listA you could risk that listA contains non-B objects. When you then try to get objects out of listB you could risk to get non-B objects out (eg an A or a C). That breaks the contract of the listB variable declaration.

listA = listB; If you could make this assignment, it would be possible to insert A and C instances into the List pointed to by listB . You could do that via the listA reference, which is declared to be of List. Thus you could insert non-B objects into a list declared to hold B (or B subclass) instances.

在此处输入图片说明

The problem

As you see the problem is in assignments . For example you can not create a method that processes List's elements that are relatives, in this manner.

The solution

Generic Wildcards to the rescue

You should use List <? extends A> List <? extends A> aka upper bound aka covariance aka producers if you are going to read .get() from the list

When you know that the instances in the collection are of instances of A or subclasses of A, it is safe to read the instances of the collection and cast them to A instances.

You can not insert elements into the list, because you don't know if the list is typed to the class A, B or C.

You should use List <? super A> List <? super A> aka lower bound aka contravariance aka consumers if you are going to insert .add() into the list

When you know that the list is typed to either A, or a superclass of A, it is safe to insert instances of A or subclasses of A (eg B or C) into the list.

You cannot read from the list though, except if it casts the read objects to Object. The elements already present in the list could be of any type that is either an A or superclass of A, but it is not possible to know exactly which class it is.

在此处输入图片说明

Please read more here

learn by example:

consider the sort() method in Collections class which use both extends and super :

    public static <T extends Comparable<? super T>> void sort(List<T> list){...}

so

why <T extends Comparable<...>> : becuase we need list items ( T ) to be a subclass of the Comparable interface.

why Comparable<? super T> Comparable<? super T> : becuase we allow the Comparable type to be a Comparable of any super type of T .

Consider

interface Comparable<T>{
    public int compareTo(T o);
}

public static <T extends Comparable<? super T>> void sort(List<T> list){...}


public static <T extends Comparable<T>> void sort2(List<T> list){...}


class A implements Comparable<A>{
    @Override
    public int compareTo(A o) {
        ...
    }
}

class B extends A {
}

    List<A> listA = new ArrayList<>();
    List<B> listB = new ArrayList<>();

    sort(listA);  //ok
    sort(listB);  //ok
    
    sort2(listA); //ok
    sort2(listB); //Error

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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