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"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_66EDIT:
I filled bug 482781 for the issue in Eclipse.
The issue with javac
is already reported as JDK-8056983, see Tunakis answer.
Eclipse does have it's own compiler, it does not use the JDK compiler (javac). However, Eclipse's compiler produces standard bytecode that complies with the Java Language Specification (JLS) and JVM Specification, so the compiled code it produces will work on any compliant JVM.
ECJ, the Eclipse Compiler for Java, is an open source incremental compiler used by the Eclipse JDT. It is an option for Liferay builds and is in many cases faster than Javac or Jikes. The jar for ECJ is included in Liferay release 4.4.
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With