17

I've read awesome "Effective Java" by Joshua Bloch. But one example in the books is left unclear to me. It's taken from chapter about generics, exact item is "Item 28: Use bounded wildcards to increase API flexibility".

In this item it's shown how to write the most universal and bulletproof (at the type system point of view) version of the algorithm of selection maximum element from collection using bounded type parameters and bounded wildcard types.

The final signature of the static method written looks like this:

public static <T extends Comparable<? super T>> T max(List<? extends T> list)

And it's mostly the same as the one of Collections#max function from standard library.

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

I understand why we need bounded wildcard in T extends Comparable<? super T> type constraint, but is it really necessary in type of the argument? It seems to me that it will be the same if we leave just List<T> or Collection<T>, isn't it? I mean something like this:

public static <T extends Comparable<? super T>> T wrongMin(Collection<T> xs)

I've written the following silly example of using both signatures and don't see any diferrence:

public class Algorithms {
    public static class ColoredPoint extends Point {
        public final Color color;

        public ColoredPoint(int x, int y, Color color) {
            super(x, y);
            this.color = color;
        }
        @Override
        public String toString() {
            return String.format("ColoredPoint(x=%d, y=%d, color=%s)", x, y, color);
        }
    }

    public static class Point implements Comparable<Point> {
        public final int x, y;

        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
        @Override
        public String toString() {
            return String.format("Point(x=%d, y=%d)", x, y);
        }
        @Override
        public int compareTo(Point p) {
            return x != p.x ? x - p.x : y - p.y;
        }
    }

    public static <T extends Comparable<? super T>> T min(Collection<? extends T> xs) {
        Iterator<? extends T> iter = xs.iterator();
        if (!iter.hasNext()) {
            throw new IllegalArgumentException("Collection is empty");
        }
        T minElem = iter.next();
        while (iter.hasNext()) {
            T elem = iter.next();
            if (elem.compareTo(minElem) < 0) {
                minElem = elem;
            }
        }
        return minElem;
    }

    public static <T extends Comparable<? super T>> T wrongMin(Collection<T> xs) {
        return min(xs);
    }

    public static void main(String[] args) {
        List<ColoredPoint> points = Arrays.asList(
                new ColoredPoint(1, 2, Color.BLACK),
                new ColoredPoint(0, 2, Color.BLUE),
                new ColoredPoint(0, -1, Color.RED)
        );
        Point p1 = wrongMin(points);
        Point p2 = min(points);
        System.out.println("Minimum element is " + p1);
    }

So can you suggest an example where such simplified signature will be inacceptable?

P.S. And why the heck there is T extends Object in official implementation?

Answer

Well, thanks to @Bohemian I've managed to figure out what's the difference between them.

Consider the following two auxiliary methods

private static void expectsPointOrColoredPoint(Point p) {
    System.out.println("Overloaded for Point");
}

private static void expectsPointOrColoredPoint(ColoredPoint p) {
    System.out.println("Overloaded for ColoredPoint");
}

Sure, it's not very smart to overload method both for superclass and its subclass, but it let us see what type of return value was actually inferred (points is List<ColoredPoint> as before).

expectsPointOrColoredPoint(min(points));     // print "Overloaded for ColoredPoint"
expectsPointOrColoredPoint(wrongMin(points)); // print "Overloaded for ColoredPoint"

For both methods inferred type was ColoredPoint.

Sometimes you want be explicit about type passed to overloaded function. You may do it a couple of ways:

You can cast:

expectsPointOrColoredPoint((Point) min(points));     // print "Overloaded for Point"
expectsPointOrColoredPoint((Point) wrongMin(points)); // print "Overloaded for Point"

Still no difference...

Or you can tell compiler what type should be inferred using syntax class.<type>method:

expectsPointOrColoredPoint(Algorithms.<Point>min(points));     // print "Overloaded for Point"
expectsPointOrColoredPoint(Algorithms.<Point>wrongMin(points)); // will not compile

Aha! Here is the answer. List<ColoredPoint> can't be passed to function expecting Collection<Point> because generics are not covariant (unlike arrays), but can be passed to function expecting Collection<? extends Point>.

I'm not sure where or who may prefer to use explicit type parameter in such case, but at least it shows where the wrongMin may be inappropriate.

And thanks to @erickson and @tom-hawtin-tackline for answers about purpose of T extends Object constraint.

east825
  • 909
  • 1
  • 8
  • 20
  • 4
    The `T extends Object & ...` causes the return type of the method after erasure to be `Object` instead of `Comparable`. This was necessary to preserve the method signature so that code compiled against the old API will still run. – erickson Jun 22 '13 at 22:16
  • Really neat note. Thanks! – east825 Jun 22 '13 at 22:49

3 Answers3

5

The difference is in the type returned, especially influenced by inference, whereby the type may be a type hierarchically between the Comparable type and the List type. Let me give an example:

class Top {
}
class Middle extends Top implements Comparable<Top> {
    @Override
    public int compareTo(Top o) {
        // 
    }
}
class Bottom extends Middle {
}

Using the signature you've provided:

public static <T extends Comparable<? super T>> T max(List<? extends T> list)

we can code this without errors, warnings or (importantly) casts:

List<Bottom> list;
Middle max = max(list); // T inferred to be Middle

And if you need a Middle result, without inference, you can explicitly type the call to Middle:

 Comparable<Top> max = MyClass.<Middle>max(list); // No cast

or to pass to a method that accepts Middle (where inference won't work)

someGenericMethodThatExpectsGenericBoundedToMiddle(MyClass.<Middle>max(list));

I don't know if this helps, but to illustrate the types the compiler as allowed/inferred, the signature would look like this (not that this compiles, of course):

public static <Middle extends Comparable<Top>> Middle max(List<Bottom> list)
Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • With the other implementation there would be no errors or warnings either, since `T` would be inferred to be `Bottom`, and there's no problem with assigning `Bottom` to `Middle` because it's a subtype – Joni Jun 22 '13 at 21:27
  • @Joni No, `T` is inferred to be `Middle`, and the list has type `? extends T`, so `List` is OK. See edit at the end if it helps. – Bohemian Jun 22 '13 at 21:30
  • We're comparing two different declarations. I am referring to the one you don't mention in the answer, which would infer `T` to be `Bottom`. – Joni Jun 22 '13 at 21:31
  • @Joni but that method was only given for comparison, but OK – Bohemian Jun 22 '13 at 21:32
  • As @Joni said, results of both calls to either `max` or `wrongMax` with `List` passed can be assigned to variable of type `Middle`. Even if `wrongMax` will infer type `Bottom` as returned type it still can be implicitly downcasted to `Middle` without any problems. – east825 Jun 22 '13 at 21:49
  • @east825 But you don't need casts with this signature. Casts are ugly and (generally) unsafe. See edits near the end regarding casts – Bohemian Jun 22 '13 at 21:56
  • @Bohemian I wrote *implicitly*, no type casts of course. I've coded your example (it's actually more clear than my, thanks) and as I said result of both calls can be assigned to varible of type `Middle` without any complains from compiler (as expected actually). Check it out: https://gist.github.com/east825/5842803. – east825 Jun 22 '13 at 22:05
  • @east825 I've added another example where inference wouldn't be enough – Bohemian Jun 22 '13 at 22:12
  • @Bohemian Oh, now I see: this compiles: `Comparable result = Algorithms.min(bottoms)` and this is not `Comparable result2 = Algorithms.wrongMin(bottoms)` because `Collection` is not covariant to `Collection`. However as @Joni wrote I can't imagine any case where such explicit type parameter for the generic method returning value of type `T` itself and not any generic type parametrized of `T` (such as Colections.emptyList()) is needed. – east825 Jun 22 '13 at 22:27
  • @Bohemian regarding `someMethodThatExpectsMiddle`. It should accept value of type `Bottom` , so it defenetily will accept value returned by `wrongMin(list)` as well - there is no need for explicit type parameter, only if it overloaded for `Bottom` as well (which is bad practice for sure). Anyway, now I see what's the point, thanks. – east825 Jun 22 '13 at 22:34
  • I don't get what you're saying. With `public static > T max(List list)` and `List list; Middle max = max(list)` it would work with no errors, warnings, or casts. – newacct Jun 23 '13 at 22:54
  • Is it just me or is `javac` better at inferring types that prevent errors than it used to be? – Andy Nov 01 '17 at 14:35
  • I have definitely seen cases in the past where Eclipse's incremental java complier accepts crazy generics but `javac` doesn't... – Andy Nov 01 '17 at 14:36
2

The difference between

T max(Collection<? extends T> coll)

and

T wrongMax(Collection<T> xs)

is that the return type of the second version is exactly the same as the collection's element type T, while in the first version T can be a super type of the element type.

The second question: the reason for T extends Object makes sure that T is a class and not an interface.


Update: A slightly more "natural" demonstration of the difference: Suppose you define these two methods:

static void happy(ColoredPoint p, Point q) {}
static void happy(Point p, ColoredPoint q) {}

And call the first one them like this:

happy(coloredPoint, min(points));
happy(coloredPoint, wrongMin(points));

The type inference engine could be able to deduce that in the first call the return type of min should be Point and the code would compile. The second call would fail to compile since the call to happy is ambiguous.

Unfortunately the type inference engine isn't powerful enough at least in Java 7, so in reality both calls fail to compile. The difference is that the first call can be fixed by specifying the type parameter as in Algorithms.<Point>min, while fixing the second call would require an explicit cast.

Joni
  • 108,737
  • 14
  • 143
  • 193
  • *while in the first version it can be a super type of `T`* Are you sure about the *super* word usage here? – Luiggi Mendoza Jun 22 '13 at 21:07
  • 1
    `T extends Object` is really meaningless, note that in the end you don't pass an interface but an object reference that can implement an interface, so every `T` will **always** be an `Object` in the end. – Luiggi Mendoza Jun 22 '13 at 21:08
  • @LuiggiMendoza It's not *totally* meaningless - as far as reflection is concerned, the super type is the *first* type mentioned in an intersection. – Bohemian Jun 22 '13 at 21:18
  • 3
    The reason why `T extends Object &` is necessary is binary (and raw type) compatibility. `max` returns a raw type of `Object` not `Comparable` (which wouldn't usually be useful in the pre-1.5 world). – Tom Hawtin - tackline Jun 22 '13 at 21:24
  • @LuiggiMendoza whoops, not "super type of T". Fixed. – Joni Jun 22 '13 at 21:29
  • so for the first question, basically you're saying, you don't know of any difference? – newacct Jun 23 '13 at 22:55
  • @newacct, what I am saying is that the difference is clear but I couldn't think of a practical demonstration. Bohemian found one: `Algorithms.wrongMax` won't compile while `Algorithms.max` does. – Joni Jun 24 '13 at 10:56
  • @Joni: But that's because you are explicitly specifying the wrong parameter. There exists a correct choice of T. `Algorithms.wrongMax` will compile. The point is, any time there is a valid choice of T for `max`, there is a valid choice of T for `wrongMax`. – newacct Jun 25 '13 at 08:48
  • Can you prove that mathematically? I think I found a counter example; see update. – Joni Jun 25 '13 at 10:31
1

Not an easy one, but i'll try to be as specific as possible:

in T max(Collection<? extends T> coll) you could pass an argument like this List<Animal> or List<Cat> or List<Dog>, and in T wrongMax(Collection<T> xs) where T is Animal you can't pass as an Argument this List<Dog>, List<Cat> of course in Runtime you could add Cat or Dog objects in List<Animal> but in compilation time you wouldn't be able to pass a subclass of Animal in the Type of the List being passed as an argument in the wrongMax method, in the other hand, in the max method you could. Sorry for my english, i still learning it :), Regards.

jac1013
  • 408
  • 3
  • 11
  • 1
    Actually in this particular case thanks to type inference of generic methods any of them (`List`, `List` or `List`) can be successfully passed to `wronMax` if their base class `Animal` implements `Comparable` or even more general `Comparable`. – east825 Jun 22 '13 at 21:56
  • @east825 are you sure about that? generics are not covariant. and we are talking about the type on the Collection. – jac1013 Jun 23 '13 at 03:19
  • Yes, read it more closely: even though in `wrongMax` type of its argument is declared as **`Collection`**, there is still *additional constraint* in front of the method **`T extends Comparable super T>`**. It means that collection of any elements those class implemens `Comparable super T>` directly or via subclassing may be passed in. – east825 Jun 23 '13 at 09:20
  • I thought that in this case **` extends T>`** it the type of argument became redundant (read it as *class that extends something that extends something that implements `Comparable`*). That's why I asked the question. – east825 Jun 23 '13 at 09:29
  • don't know if we are talking about the same thing, but when we define `T extends Comparable super T>` before the return type still `List` won't pass as an argument if the argument is define as `Collection` because `? super T` means any supertype of T with T inclusive, `Dog` is `Comparable` yes, but it is `Comparable` because it's a subclass of Animal and it won't pass the `? super T` – jac1013 Jun 23 '13 at 14:09
  • @user1322142: You're wrong. If you pass a `List`, then `T = Dog` works. – newacct Jun 23 '13 at 22:58
  • Yes, i realize it, you are right @east825, thanks for clarify the answer. – jac1013 Jun 24 '13 at 00:16