简体   繁体   中英

Java wildcard with super unexpected behaviour

I wrote a class Fruit which implements the Comparable interface and 2 subclasses: Apple and Orange. I wrote a method that returns the maximum between 2 fruits (whatever it means).

Note that I did NOT use any wildcards with super.

I thought that the method max would fail because the Comparable interface is not implemented directly by Apple or Orange.

Question: Why is it suggested to use this form of wildcard:

<T extends Comparable<? super T>>

if it works also without super?

Here is the code:

package main;

//Note that I did not use the super wildcard: <T extends Comparable<? super T>>
class Max {

  public static <T extends Comparable<T>> T getMax(T x, T y) {
    return x.compareTo(y) >= 0 ? x : y;
  }
}

class Fruit implements Comparable<Fruit> {
    public String name;

    public Fruit(String name) {
        this.name = name;
    }

    @Override
    public int compareTo(Fruit other) {
        return name.compareTo(other.name) == 0 ? 0 :
               name.compareTo(other.name) > 0 ? 1 : -1;
    }

}

class Apple extends Fruit {
    String name;

    public Apple(String name) {
        super(name);
    }

}

class Orange extends Fruit {
    String name;

    public Orange(String name) {
        super(name);
    }

}

public class Main {

    public static void main(String[] args) {

        Apple a = new Apple("apple");
        Orange o = new Orange("orange");
        Fruit f = Max.getMax(a, o); //It should not be allowed because T does not implement Comparable directly
        System.out.println(f.name);

  }
}

In your case, T can be chosen to be Fruit , and the statement Fruit f = Max.getMax(a, o); would type-check correctly. Therefore, it works.

Max.getMax() has parameter types T , and instances of subtypes of T are also instances of T , so the method accepts any subtypes of T as arguments.

Note, however, that your method still has the problem that T can only be inferred to be Fruit , and not Apple , so you cannot return an Apple :

Apple a = Max.getMax(apple1, apple2);

However, consider something where the T is a generic type parameter:

public static <T extends Comparable<T>> T getMax(List<T> xs) {
  //...
}

Generics are not covariant, so this method can only accept List<Fruit> , but not List<Apple> or List<Orange> , even though Apple s can compare to Apple s, etc.

If you change it to:

public static <T extends Comparable<? super T>> T getMax(List<T> xs) {
  //...
}

then it work would for List<Apple> and List<Orange> .

As Fruit implements Comparable and Apple and Orange extend Fruit then they simply inherit the compareTo implementation from Fruit .

You could override that compareTo method in the Apple and Orange subclasses if you wished.

I think it is your understanding of inheritance here rather than your understanding of the > symbol.

<T extends Comparable<? super T>>

Here you are defining that the type T extends Comparable, so you can safely call the compareTo method on the objects.

Both objects you passed to the method DO extend comparable (as they extend Fruit which implements Comparable)

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