java - 为什么 javac 需要引用类的接口(interface)而 ECJ 不需要?

标签 java java-8 javac ecj

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

是什么让 JDK 需要编译父类(super class)型,而 ECJ 编译就很好?

它不是默认方法,正如评论的那样in the bug report , 和 compatibility guide也同意:

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.


更新:

  • 类似问题:Java 8 interface/class loader changes?
  • I.doit()default 还是纯抽象方法都没有关系,行为是一样的
  • I.doit() 是否在 O 中被覆盖当然很重要;如果没有被覆盖,那么 ECJ 也会到达 I 以获取 doit()
  • 的定义

接口(interface)(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();
    }
}

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

最佳答案

似乎对Compatibility Guide for JDK 8的目的有误解.

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

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

如解释here ,形式化过程描述为一个方法调用搜索所有可能适用的成员方法,但并没有说在保证程序正确性的情况下不能走捷径。

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

关于java - 为什么 javac 需要引用类的接口(interface)而 ECJ 不需要?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45030100/

相关文章:

java - 制作不可变的 Java 对象

java - 是否可以在 JavaFX 场景中显示 VolatileImage?

java - 任何其他更好的方法来处理使用Java Stream或Optional.ofNullable

java - 有没有办法可以在Java中打印两个纪元时间之间的所有天数?

java - "Can not set org.eclipse.aether.spi.log.Logger"带有自定义 Maven 插件

java - 简化便利构造函数

java - Java 8 JDK 中是否有默认方法及其用法的好示例?

java - 用 javac 编译并将 double 转换为 float

java - 为什么 `javac -cp` 不需要 `.` 而 `java -cp` 需要?

java - 如何限制Java SDK小版本和主版本之间的应用程序?