当ECJ没有时,为什么javac需要引用类的接口?

pwe*_*wes 2 java javac java-8 ecj

在编译a时Client,它使用一些接口I(例如O)的实现,类文件I也必须存在于类路径中.奇怪的是,这只是一个例子javac,因为Eclipse编译器(ECJ)不需要I编译.

是什么让JDK 需要超类型进行编译,ECJ编译得很好?

它不是默认方法,如错误报告中所评论的那样,兼容性指南也同意:

在针对另一个实现在另一个类文件中定义的接口的类编译类时,这样的类文件(其中定义了接口)必须在编译期间由javac使用的类路径中可用.这是JDK 8的新要求 - 如果不这样做将导致编译错误.


更新:

  • 类似的问题:Java 8接口/类加载器的变化?
  • 无论I.doit()default简单的抽象方法都没关系,行为是一样的
  • 无论是否I.doit()被覆盖,当然都很重要O; 如果没有覆盖,那么ECJ也会达到I定义doit()

接口(api/a/I.java):

package a;
public interface I {
    default void doit() {
        System.out.println("In I");
    }
}
Run Code Online (Sandbox Code Playgroud)

实施(impl/b/O.java):

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

客户(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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

一个日志:

$ 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
Run Code Online (Sandbox Code Playgroud)

Hol*_*ger 6

似乎对JDK 8兼容性指南的目的存在误解.

这不是一个关于编译器或环境如何规范应该的行为,这是一个关于JDK文档如何循规蹈矩,以发现潜在的兼容性问题.这并不意味着另一个编译器必须表现出完全相同的行为.

它之所以提到特定行为,是因为javac它将其行为从JDK 7改为JDK 8,这可能会导致兼容性问题.

正如这里所解释的那样,正式过程被描述为搜索方法调用的所有可能适用的成员方法,但是并不表示当可以保证程序的正确性时不允许快捷方式.

因此bug报告已经关闭,因为新行为在规范范围内,不一定是因为替代行为会违反它.

  • 我认为这两种行为都符合规范.我认为,实现它的主要原因就像正式规范一样,没有任何快捷方式,是为了使编译器的代码更简单,因为整个过程非常复杂.如果ECJ开发人员认为,他们仍然可以通过支持该快捷方式维护代码,他们可能会继续这样做.在任何一种情况下,我们都不应该认为这种捷径的存在是一种保证行为. (5认同)