4

I have this code which compiles fine when compiled as Java 7 (Eclipse compiler) but fails when I set the project settings to Java 8:

package scratch;

class Param<T extends Comparable<T>> {
  public Comparable<?> get() {
    return null;
  }
}

public class Condition<T extends Comparable<T>> {
  public static <T extends Comparable<T>> Condition<T> isInRange(T lower, T upper) {
    return null;
  }

  public void foo() {
    Comparable bound = null;                 // Line 15 
    Param<?> param = new Param<Double>();
    Condition.isInRange(param.get(), bound); // Line 17
  }
}

In Java 7, I get these warnings:

  • line 15: Comparable is a raw type. References to generic type Comparable should be parameterized
  • line 17: Type safety: Unchecked invocation isInRange(Comparable, Comparable) of the generic method isInRange(T, T) of type Condition

When I add <?> in line 15, the warning is gone, but I then get an error in line 17:

Bound mismatch: The generic method isInRange(T, T) of type Condition is not applicable for the arguments (Comparable, Comparable). The inferred type Comparable is not a valid substitute for the bounded parameter >

Does anyone know what exactly causes this incompability?

PS: I added these ugly casts to make the code compile under both versions of Java:

    Condition.isInRange((Comparable)param.get(), (Comparable) bound);
Axel
  • 13,939
  • 5
  • 50
  • 79
  • 2
    The eclipse compiler has some bugs with regards to generics, so you should test with `javac`. – Kayaman Jul 05 '16 at 10:39
  • 3
    This has nothing to do with the wildcard, but with the *raw type* of the `bound` variable. In Java-7, this effectively turns off all checks regarding the `isInRange` method invocation. In Java-8, the target typing still detects the invalidity of the nested invocation, similar to [this scenario](http://stackoverflow.com/a/26285613/2711488). I don’t understand, why you think your type casts to the raw `Comparator` are uglier than the initial usage of the raw type. Of course, the second type cast is nonsensical as `bound` is already a raw `Comparable`. – Holger Jul 05 '16 at 10:39
  • 2
    @Kayaman: in this case, Eclipse is perfectly in line with `javac`. – Holger Jul 05 '16 at 10:47
  • @Kayaman: I tested with javac and got the same reasults. – Axel Jul 05 '16 at 11:12
  • @Holger: Yes, both versions are ugly. Still have no idea how to make it look less ugly though. – Axel Jul 05 '16 at 11:13
  • 2
    Why are you using raw types? Don't use raw types, unless you absolutely have to because you're dealing with old (pre-generics) code that you can't change. – Jesper Jul 05 '16 at 11:17
  • 2
    Well, there is no solution derivable from this example code. The method `isInRange` can’t accept a `Comparable>`, as that type is incompatible with the required parameter type. You can inhibit the generic type checking by incorporating raw types, but the correct solution would be to prevent getting a `Comparable>` in the first place. But we can’t derive from your example, why you have these incomplete types… – Holger Jul 05 '16 at 11:19

1 Answers1

0

Firstly, I am not an expert on generics so my answer could be incorrect. I believe the issue is the way the isInRange method is defined.

<T extends Comparable<T>>

This is a recursive definition, which is not a problem in itself. Look at classes like Double, Long. I believe that because of how recursive definition is resolved, the types to the method (isInRange) must be a class that implements Comparable that uses itself as the generic for Comparable; e.g., class C implements Comparable<C>

Christopher Z
  • 899
  • 1
  • 12
  • 32