【发布时间】:2017-07-11 09:08:45
【问题描述】:
编译Client 时,它使用接口I 的某些实现(例如O),I 的类文件也必须存在于类路径中。奇怪的是,这只是javac 的情况,因为Eclipse 编译器(ECJ)不需要I 进行编译。
是什么让 JDK 需要 超类型用于编译,而 ECJ 编译得很好?
这不是默认方法,正如in the bug report 所评论的那样,compatibility guide 也同意:
当针对实现在另一个类文件中定义的接口的另一个类编译一个类时,此类文件(定义接口的位置)必须在编译期间 javac 使用的类路径中可用。这是 JDK 8 的新要求 - 不这样做将导致编译错误。
更新:
- 类似问题:Java 8 interface/class loader changes?
- 不管
I.doit()是default还是普通抽象方法,行为都是一样的 -
I.doit()是否被O覆盖当然很重要;如果没有被覆盖,那么 ECJ 也会到达I以获取doit()的定义
接口(@987654337@):
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
【问题讨论】:
-
你确定在 ECJ 的情况下它不在类路径上吗?
-
是的,查看日志。这是一个与更复杂的案例分开的测试案例,其中 Eclipse 可以很好地编译而没有传递依赖,而 javac(从 Gradle CI 脚本运行)抱怨。
-
我想看看当你从
O类中删除doit()时 ECJ 会做什么,但其他所有内容都保持不变...... -
实际上可能是 ECJ 错误。
-
本兼容性指南描述了
javac的实际行为,它不是关于编译器必须如何工作的规范。不是javachas been noticed already的变化行为。