Generics and lambdas - different behavior in javac and Eclipse compiler
Asked Answered
R

1

8

Note: I found multiple questions pointing out differences between javac and the Eclipse compiler, but as far as I could see all of them discuss other issues.

Suppose we have this method:

public static <T, U> void foo(Supplier<T> a, Function<T, U> b, Consumer<U> c)
{
    c.accept(b.apply(a.get()));
}

I found different behavior between javac and the Eclipse Java compiler when compiling calls to this method and I'm not sure which of the two is right.


A simple use of this method could be:

// variant 1
foo(
    () -> Optional.of("foo"),
    value -> value.get(),
    value -> System.out.println(value));

The compiler should be able to bind T to Optional<String> using the first argument and U to String using the second. So this call should be valid (in my opinion).

This compiles fine using javac but fails to compile using Eclipse:

Type mismatch: cannot convert from void to <unknown>

Adding a type argument to the first argument (() -> Optional.<String> of("foo")) makes it compile in Eclipse too.

Question: From a specification point of view, is Eclipse correct in rejecting this call (and why (not))?


Now suppose we want to throw a custom (runtime) exception, if the Optional is empty:

// variant 2
foo(
    () -> Optional.of("foo"),
    value -> value.orElseThrow(() -> new RuntimeException()),
    value -> System.out.println(value));

This is rejected by both, javac and the Eclipse compiler, but with different error messages:

  • javac: "unreported exception X; must be caught or declared to be thrown"
  • Eclipse compiler: "Type mismatch: cannot convert from void to <unknown>"

When I add the type argument to the first argument as above, Eclipse succeeds in compiling while javac still fails. When I add <RuntimeException> as type argument to the second argument, it's the other way around, Eclipse fails and javac succeeds.

Question: Again, are the compilers right in rejecting this call and why?


In my opinion both variants should compile fine without additional hints by using type arguments. If so I'll fill one bug report for javac (regarding the "unreported exception") and one for the Eclipse compiler (regarding the "type mismatch"). But first I want to be sure the specification shares my point of view.

Versions used:

  • javac: 1.8.0_66
  • Eclipse JDT: 3.11.1.v20151118-1100

EDIT:

I filled bug 482781 for the issue in Eclipse.

The issue with javac is already reported as JDK-8056983, see Tunakis answer.

Refutation answered 21/11, 2015 at 19:54 Comment(6)
Would blame eclipse when in doubt :) Type inference is very complicated, the whole lambda syntax is still quite new. Eclipse has several bugs fixed but some left in the current release, related to edge cases like this.Handal
Eclipse Mars ECJ compiler is really buggy compared to the latest Luna when it comes to generic expansion. I already stumbled with at least three cases when ECJ 3.11 fails or even stuck in infinite loop while javac and ECJ 3.10 compile correctly. That's why I'm still using Luna.Enamour
The Eclipse bug has already been fixed for 4.6 M1 via bugs.eclipse.org/470826 which is also scheduled for back port to mars.2Trabzon
@TagirValeev I couldn't find any bugs reported by you against ecj. Do you care to share your examples so that Eclipse can be improved? You may also want to try a recent milestone build, since some six bugs in type inference have been fixed even since 4.5.1.Trabzon
@TagirValeev the latest ecj.jar (which contains the batch compiler) can always be found at download.eclipse.org/eclipse/downloads - just select a version and on the next page navigate to "JDT Core Batch Compiler", HTHTrabzon
@StephanHerrmann, did not know, thank you. Here's another report.Enamour
M
5

Yes, you're right in every aspect. I honestly wouldn't be able to link to specific lines of the JLS about this: type inference is a whole chapter.

Disclaimer: I tested using Eclipse Mars 4.5.1 and JDK 1.8.0_60.


Variant 1 should compile and Eclipse has a bug here. I couldn't find anything related to this in their Bugzilla so you could go ahead and file it. You can assure yourself that it should compile if you reduce your example to this:

public static <T> void foo(Supplier<T> a) {
    a.get();
}

foo(() -> Optional.of("foo"));

This compiles fine both with Eclipse and javac. Adding parameters does (should) not change the type inferred for T during compilation.


Variant 2 does not compile for javac and this is indeed a bug, as reported in JDK-8056983. The compiler should be able to infer that X is RuntimeException. As to why Eclipse is still not able to compile this, again, I couldn't find anything in their Bugzilla so feel free to report this!

Misbeliever answered 21/11, 2015 at 20:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.