Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't the eclipse java compiler (ecj) compile this?

Tags:

java

javac

ecj

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;
    }


}
like image 822
Johannes Kuhn Avatar asked Jun 23 '18 01:06

Johannes Kuhn


People also ask

What is ECJ Eclipse?

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.

What Java compiler does Eclipse use?

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.


1 Answers

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

like image 57
Stephan Herrmann Avatar answered Oct 07 '22 09:10

Stephan Herrmann