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

时间:2017-07-11 09:08:45

标签: java java-8 javac ecj

编译使用接口Client的某些实现的I时(例如O),类I的类文件也必须存在于类路径中。奇怪的是,这只是javac的一种情况,因为Eclipse编译器(ECJ)不需要I进行编译。

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

这不是默认方法,如评论in the bug reportcompatibility guide也同意:

  

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

更新

  • 类似的问题:Java 8 interface/class loader changes?
  • 如果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");
    }
}

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

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

客户(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

日志:

$ 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

2 个答案:

答案 0 :(得分:6)

似乎对Compatibility Guide for JDK 8的目的存在误解。

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

它提到特定行为的原因是,因为javac将其行为从JDK 7更改为JDK 8,这可能会导致兼容性问题。

正如here所解释的那样,正式过程被描述为搜索方法调用的所有可能适用的成员方法,但它并没有说当程序的正确性时,不允许使用快捷方式。保证。

所以that bug report已经关闭,因为新行为在规范范围内,不一定是因为替代行为会违反它。

答案 1 :(得分:3)

如果O未覆盖doit(),该怎么办?

然后Client必须仍然可以致电doit(),因为它是I合同的一部分,但O中缺少此信息}&#39;类文件。

您可能会问&#34;为什么不在O的类文件中包含默认方法定义?&#34;。这将首先打破引入默认方法的意图:使用Java-8之前的编译器编译的类仍应在Java 8中工作,并且接口新方法应该可用。