Search code examples
javajava-8javacecj

Why javac requires an interfaces of referenced classes when ECJ does not?


When compiling a Client, which uses some implementation of interface I (e.g. O), the classfile for I must also be present on the classpath. What is strange, this is only a case for javac, as Eclipse compiler (ECJ) does not require I for compilation.

What makes JDK requires supertypes for compilation, where ECJ compiles just fine?

It is not default methods, as was commented in the bug report, and the compatibility guide also agrees:

When compiling a class against another class implementing an interface which is defined in yet another class file, such class file (where interface is defined) must be available in the class path used by javac during compilation. This is a new requirement as of JDK 8 - a failure to do so will result in a compilation error.


UPDATE:

  • Similar question: Java 8 interface/class loader changes?
  • It doesn't matter if I.doit() is default or plain abstract method, the behavior is the same
  • It of course matters whether I.doit() is overridden in O or not; if not overriden, then ECJ also reaches to I for the definition of doit()

The interface (api/a/I.java):

package a;
public interface I {
    default void doit() {
        System.out.println("In I");
    }
}

Implementation (impl/b/O.java):

package b;
public class O implements a.I {
    public void doit() {
        System.out.println("In O");
    }
}

Client (client/c/Client.java):

package c;
import b.O;
public class Client {
    public void test() {
        O o = new O();
        o.doit();
    }
    public static void main(String[] args) {
        new Client().test();
    }
}

A Makefile:

# bug report:
#   Javac requires interface on classpath when using impl
#   https://bugs.openjdk.java.net/browse/JDK-8055048
#
# compatibility guide:
#   http://www.oracle.com/technetwork/java/javase/8-compatibility-guide-2156366.html
#   (Synopsis: Interfaces need to be present when compiling against their implementations)
# 
# ECJ downloaded from:
#   http://central.maven.org/maven2/org/eclipse/jdt/core/compiler/ecj/4.6.1/ecj-4.6.1.jar

ifeq (${V}, ecj)
JC := java -jar ecj-4.6.1.jar -8
else
JC := javac -source 1.8 -target 1.8 -implicit:none
endif

rebuild: clean lib client

lib: api/a/I.class impl/b/O.class

client: lib client/c/Client.class

clean:
    rm -f api/a/I.class impl/b/O.class client/c/Client.class

%.class: %.java
    ${JC} ${OPT} $<

impl/b/O.class: OPT = -cp api
client/c/Client.class: OPT = -cp impl

A log:

$ make V=ecj rebuild                                                                                                                                                                                               
rm -f api/a/I.class impl/b/O.class client/c/Client.class
java -jar ecj-4.6.1.jar -8  api/a/I.java
java -jar ecj-4.6.1.jar -8 -cp api impl/b/O.java
java -jar ecj-4.6.1.jar -8 -cp impl client/c/Client.java

$ make rebuild
rm -f api/a/I.class impl/b/O.class client/c/Client.class
javac -source 1.8 -target 1.8 -implicit:none  api/a/I.java
javac -source 1.8 -target 1.8 -implicit:none -cp api impl/b/O.java
javac -source 1.8 -target 1.8 -implicit:none -cp impl client/c/Client.java
client/c/Client.java:8: error: cannot access I
                o.doit();
                 ^
  class file for a.I not found
1 error
make: *** [client/c/Client.class] Error 1

Solution

  • There seems to be a misunderstanding on the purpose of the Compatibility Guide for JDK 8.

    This is not a specification about how a compiler or environment should behave, it’s a document about how the JDK does behave, to spot potential compatibility problems. This does not imply that another compiler has to exhibit exactly the same behavior.

    The reason why it mentions that particular behavior, is, because javac changed its behavior from JDK 7 to JDK 8 and this can cause compatibility problems.

    As explained here, the formal process is described as searching for all potentially applicable member methods for a method invocation, but it doesn’t say that short-cuts aren’t allowed when the correctness of a program can be guaranteed.

    So that bug report has been closed, because the new behavior is within the specification, not necessarily because an alternative behavior would violate it.