davmac
davmac

Reputation: 20631

Eclipse or Javac bug; lambda type inference

The following code compiles with javac, and with Eclipse 4.6.1/4.6, but produces an error in Eclipse 4.6.2:

package ecbug;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Foo
{
    class A
    {
        public int getStart() { return 0; }
    }

    void someMethod(List<A> toRemove)
    {
        Collections.sort(toRemove, Comparator.comparing(t -> -t.getStart()));
    }
}

Eclipse 4.6.2 complains, under -t.getStart(), that there is a Type mismatch: cannot convert from int to Comparable<? super Comparable<? super U>>.

I would think that the arguments to Comparator.comparing(...) should be a Comparable<T> with T = A, with functional method compareTo which returns int. Eclipse seems to believe that the lambda function should return Comparable<? super Comparable<? super U>>, however.

I strongly suspect an Eclipse bug, but there are certainly cases where Eclipse has correctly implemented the language specification and javac hasn't, so it seems worth asking: is this an Eclipse bug or a javac bug? Can any language-lawyers out there point out the relevant parts of the language spec?

Possibly related questions, which in my eyes are not duplicates:

Java 8 Stream flatMap and group by code compiler error - similar error message, but unclear if it is exactly the same issue; Answer claims that it is an Eclipse bug but provides no bug link nor JLS quotations; refers to old Eclipse version.

Why didn't this java 8 example using type inference compile in Eclipse? - similar to previous

Java Stream collect after flatMap returns List<Object> instead of List<String> - again, may be a different issue; comments claim Eclipse problem but do not justify via reference to JLS nor provide a link to an Eclipse bug report.

Upvotes: 2

Views: 1186

Answers (1)

Holger
Holger

Reputation: 298143

Your explanation doesn’t hit it. The argument to Comparator.comparing(...) (single argument version) should not be a Comparable<T>, but rather a Function<? super T,? extends U>, whereas T := A, but U being U extends Comparable<? super U>.

So when you say

Eclipse seems to believe that the lambda function should return Comparable<? super Comparable<? super U>>

you are right regarding Eclipse’s expectation and Eclipse is right in expecting that, too.

But your function returns an int value that is supposed to be compared and when you box that value to Integer, you have a type that fulfills the expected U extends Comparable<? super U> constraint, as Integer implements Comparable<Integer>. In other words, U should be inferred to be Integer, but apparently, this specific Eclipse version fails to do this due to the required boxing of int to Integer.


As a side note, when you want to compare an int property, you might want to use Comparator.comparingInt(...) anyway. With this factory, the returned comparator avoids boxing of int to Integer altogether.

Additionally, you should not reverse an integer ordering by negation. The problem is that
-Integer.MIN_VALUE == Integer.MIN_VALUE, as trying to negate the smallest possible int value causes an overflow evaluating to the smallest int value again, instead of the biggest one. Using negation for reversing the order may work in a lot of situations, in some of them it might be justified as this special value can be precluded, however, it creates a bad habit, that can backfire awfully in a scenario where the situation can occure, of course, only rarely and usually only at the customer…

The correct idiom would be

Collections.sort(toRemove, Comparator.comparingInt(A::getStart).reversed());

which works by swapping the two elements to compare, which is valid in all scenarios and has no performance drawbacks.

Upvotes: 2

Related Questions