Majid Azimi
Majid Azimi

Reputation: 5745

Problem with <T extends Comparable<? super T>>

I have a three class: 1.class Algorithm having max() finding maximum value in a Collection:

public class Algorithm {

    public static <T extends Comparable<T>> T max(Collection<? extends T> coll) {
        T max = coll.iterator().next();

        for (T elm : coll) {
            if (max.compareTo(elm) < 0)
                max = elm;
        }

        return max;
    }
}

2.Class Fruit:

public class Fruit implements Comparable<Fruit> {
    private String name;
    private int size;

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

    public int compareTo(Fruit that) {
        if (size < that.size)
            return -1;
        else if (size == that.size)
            return 0;
        else
            return 1;
    }
}

3.class Apple extending Fruit:

public class Apple extends Fruit {
    public Apple(int size) {
        super("Apple", size);
    }
}

Now the question is this:

public class Main
{
    public static void main(String[] args) {        
        Apple a1 = new Apple(10);
        Apple a2 = new Apple(34);

        List<Apple> apples = Arrays.<Apple>asList(a1, a2);

        System.out.println(Collections.max(apples).size);
    }
}

According to this post Java - Syntax Question: What is I should wrote it this way: public static <T extends Comparable<? super T>> T max(Collection<? extends T> coll). But it is working fine now.Why? Class Apple does not implement Comparable<Apple> and there is no super.

[UPDATE]
Java Generics and Collections Book says:

Without the super wildcard, finding the maximum of a List<Apple> would be illegal, even though finding the maximum of a List<Fruit> is permitted.

Upvotes: 4

Views: 5732

Answers (4)

let's try to see what happens when you remove <? super T>

public class CollectionMinSignature {
    
    public static <T extends Object & Comparable</*? super*/ T>> T min(Collection<? extends T> coll) {
    
        Iterator<? extends T> i = coll.iterator();
        T candidate = i.next();

        while (i.hasNext()) {
            T next = i.next();
            if (next.compareTo(candidate) < 0)
                candidate = next;
        }
        return candidate;
    }
    
    public static class A implements Comparable<A> {
        private int i;
        public A (int i) {
            this.i = i;
        }
        @Override
        public int compareTo(A a) {
            return this.getVal() - a.getVal();
        }
        public String toString() {
            return Integer.toString(i);
        }
        public int getVal() {
            return this.i;
        }
    }
    public static class B extends A {
        public B(int i) {
            super(i);
        }
    }
    
    public static void main(String[] args) {
    
    Collection<B> coll = new ArrayList<B>(List.of(new B(0), new B(1), new B(-1)));
    
    B b = min(coll); /*it doesn't work*/
    B b2 = Collections.min(coll); /*it works*/

Upvotes: 0

onlyahobo
onlyahobo

Reputation: 3

sorry for bringing this back but I think it's important. Has your code stopped working properly when changed from Collections.max() to Algorithm.max()? I've done a simmilar test in jdk8 and I don't understand why it's working fine while according to Java Generics and Collections it should not.

I have a Fruit abstract class (implementing Comparable):

public abstract class Fruit implements Comparable<Fruit> {
    private String name;
    private int size;

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

    public int compareTo(Fruit that) {
        if (size < that.size)
            return -1;
        else if (size == that.size)
            return 0;
        else
            return 1;
    }
}

Then I have a Apple extending Fruit class:

public class Apple extends Fruit {
    public Apple(String name, int size) {
        super(name, size);
    }
}

And finally:

 public class GenericsTest {

    @Test
    public void test1() {
        final Apple a1 = new Apple("apple1", 50);
        final Apple a2 = new Apple("apple2", 70);
        final Apple a3 = new Apple("apple3", 34);

        final List<Apple> apples = Lists.newArrayList(a1, a2, a3);

        System.out.println(GenericsTest.max(apples).getSize());
    }

    private static <T extends Comparable<T>> T max(Collection<? extends T> coll) {
        T max = coll.iterator().next();

        for (T elm : coll) {
            if (max.compareTo(elm) < 0)
                max = elm;
        }

        return max;
    }
}

The code is working while there is no ? super T in the max method's signature and the List is of Apple type. According to the quote you mentioned it shouldn't be working. I seem to be puzzled here...

Upvotes: 0

Adam Paynter
Adam Paynter

Reputation: 46918

Suppose we changed the max method to this:

<T extends Comparable<T>> T max(Collection<? extends T> coll)

You would not be able to get the max of a List<Apple> because Apple does not implement Comparable<Apple>, it implements Comparable<Fruit>. But you and I know perfectly well that an Apple knows how to compare itself to another Fruit because it inherited that functionality.

We fix the problem by changing the declaration of max to this:

<T extends Comparable<? super T>> T max(Collection<? extends T> coll)

This means that we accept any class T such that:

  1. T implements Comparable<T>, or...
  2. T implements Comparable<X> for some X such that X is a super class of T

In order to find the max, we must be sure that any instance of T can safely accept another instance of T as an argument to its compare method.

In the first scenario, it is obvious that any instance of T can safely accept another instance of T as an argument to its compare(T) method.

In the second scenario, any instance of T can safely accept another instance of T as an argument to its compare(X) method because all instances of T are also instances of X.

Your example illustrates the second scenario, where T corresponds to Apple and X corresponds to Fruit.

Upvotes: 6

Sergey Aslanov
Sergey Aslanov

Reputation: 2415

You use Collections.max(apples) instead of Algorithm.max.

Collections.max has slightly different declaration:

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)

Upvotes: 1

Related Questions