I have the following code:
package test;
import java.util.stream.IntStream;
public class A {
public static void main(String[] args) {
IntStream.range(0, 10).mapToObj(n -> new Object() {
int i = n;
}).mapToInt(o -> o.i).forEachOrdered(System.out::println);
}
}
This code works fine when compiled with javac 1.8.0_101, and produces the number 0 to 9 as expected.
But when I use this code in eclipse, it tells me that at o.i
:
i cannot be resolved or is not a field
And producing an error when executing this:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
i cannot be resolved or is not a field
at test.A.main(A.java:9)
Why do I need to use javac to compile this code?
And how do I get eclipse to behave?
Edit:
I did some tests and it works in ecj as long as I don't create the instance in a lambda:
package test;
import java.util.Optional;
import java.util.function.Supplier;
public class B {
public static void main(String[] args) {
// This works fine:
System.out.println(new Object() {
int j = 5;
}.j);
// This also
System.out.println(trace(new Object() {
int j = 5;
}).j);
// Also no problem
System.out.println(unwrapAndTrace(Optional.of(new Object() {
int j = 5;
})).j);
// Lambdas work:
System.out.println(((Supplier & Serializable) () -> new Object()).get());
// This doesn't work.
System.out.println(invokeAndTrace(() -> new Object() {
int j = 5;
}).j);
}
public static <T> T trace(T obj) {
System.out.println(obj);
return obj;
}
public static <T> T invokeAndTrace(Supplier<T> supplier) {
T result = supplier.get();
System.out.println(result);
return result;
}
public static <T> T unwrapAndTrace(Optional<T> optional) {
T result = optional.get();
System.out.println(result);
return result;
}
}
ecj is the batch compiler from Eclipse and is available as ecj. jar. Since 3.3, this jar also contains the support for jsr199 (Compiler API) and the support for jsr269 (Annotation processing). In order to use the annotations processing support, a 1.6 VM is required.
Eclipse Java compiler is an incremental Java builder The Java compiler built in Eclipse is a part of JDT Core component (JDT: Java Development Tool). An incremental compiler automatically compiles code when changes are detected.
This is a bug in ecj, recently reported also as Bug 535969.
In a nutshell: to avoid a hard technical problem, the compiler drops the anonymous class during type inference, replacing it with its super class (in specific situations, not always). With this, the result of mapToObj()
is seen as Stream<Object>
where indeed the anonymous class should be used. Original assessment, that this information loss would be OK (because nobody can mention the anonymous class) is proven wrong by the example in this question.
EDIT: The bug has been fixed via the pre-existing report Bug 477894
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