Generics super vs. extends

2019-02-05 09:14发布

问题:

Just when I thought I finally understood generics, I came across the following example:

public class Organic<E> {
          void react(E e) { }
          static void main(String[] args) {
            //1: Organic<? extends Organic> compound = new Aliphatic<Organic>(); 
            //2: Organic<? super Aliphatic> compound = new Aliphatic<Organic>(); 
           compound.react(new Organic());
           compound.react(new Aliphatic());
           compound.react(new Hexane());
 } }
 class Aliphatic<F> extends Organic<F> { }
 class Hexane<G> extends Aliphatic<G> { }

It says, if line 1 is uncommented, the following will not compile:

  compound.react(new Organic());  
  compound.react(new Aliphatic());  
  compound.react(new Hexane());

while if line 2 is ucommented, the following will not compile:

compound.react(new Organic());

In the second example, Aliphatic and it's supertypes are allowed. So why isn't Aliphatic allowed?

In the first example, why isn't new Organic allowed??

1st compiler error:

- The method react(capture#1-of ? extends Organic) in the type Organic<capture#1-of ? extends Organic> is not applicable for the arguments (Organic)
- The method react(capture#2-of ? extends Organic) in the type Organic<capture#2-of ? extends Organic> is not applicable for the arguments (Aliphatic)
- The method react(capture#3-of ? extends Organic) in the type Organic<capture#3-of ? extends Organic> is not applicable for the arguments (Hexane)

2nd compiler error:

- The method react(capture#1-of ? super Aliphatic) in the type Organic<capture#1-of ? super Aliphatic> is not applicable for the arguments  (Organic)

回答1:

Your first declaration

Organic<? extends Organic> compound

means that compound could be an Organic<SomeSubtypeOfHexane> (since Aliphatic extends Organic, Hexane extends Aliphatic and SomeSubtypeOfHexane extends Hexane).

In that case, compound.react(new Organic()), compound.react(new Aliphatic()) and compound.react(new Hexane()) would lead to a type error, since E in compound must be a SomeSubtypeOfHexane (or subtype thereof).


Your second declaration

Organic<? super Aliphatic> compound

means that compount could be an Organic<Aliphatic>.

In that case compound.react(new Organic()) would lead to a type error, since E must be an Aliphatic (or subtype thereof).


Remember that declaring a variable using A<? extends B> or A<? super B>

  • extends the amount of objects that can be assigned to it, and, in consequence,
  • restricts what can be done with the variable.

Since the exact type of the class is unknown (only a constraint is known), the compiler has to err on the side of safety and disallow certain operations that are either not co- or contravariant. (If you are not already familiar with it, Co- and contravariance is the scientific background of these types of generics.)



回答2:

The topic question is also discussed in several other places like:

  • Generic upper bounded wildcard instantiation known at run time
  • Generics with extends
  • CodeRanch: Generics and use of wildcard
  • CodeRanch: Problem in Generics
  • Oracle: Guidelines for Wildcard Use

It is actually an question from the book for the Java certificate preparation, from the last test (Question 34). The preparation book is based on the lessons book.

Even with the explanation here and under the other links and the writing in the book the solution was not clear for me because the explanations are mostly based on the List interface and reading that I thought it is some inner collection specific soluton.

But if you see the definition of the List interface and the add-method on one side and the the Organic class with its react-method on other side you will notice that they are defined in the similar way.

public interface List<E> extends Collection<E> {
   ...
   boolean add(E e);
   ...
}

public class Organic<E> {
    ...
    void react(E e) { }
   ...
}                                               

So all explanation based on the List interface examples which you can find anywhere are valid for this question too.

List<? extends String> list1 = new ArrayList<String>();
List<? super String> list2 = new ArrayList<String>();
list1.add(new String()); //The method add(capture#1-of ? extends String) in the type List<capture#1-of ? extends String> is not applicable for the arguments (String) - // compile-time error
list2.add(new Object()); //The method add(capture#2-of ? super String) in the type List<capture#2-of ? super String> is not applicable for the arguments (Object) - // compile-time error

Take a look at the explanation on this one:

  • Oracle: Guidelines for Wildcard Use


回答3:

I think you misunderstood what you set as a parameter in your react() method

Try changing

void react(E e) { }

to

void react(Organic<E> e) { }

to see the difference. You are looking for objects: Organic<E> Aliphetic<E> Hexane<E>

Not Organic<E> as E

Nor Aliphetic<E> as E

Nor Hexane<E> as E



回答4:

You made some mix.
Organic<E> does equal in its type to Organic.
You cannot perform
new Organic()); You must at least consider performing
new Organic<Object>()); And this is just one compile error I can think of
This of course is valid for your other object instantiations.