为什么 javac 需要引用 类 的接口而 ECJ 不需要?
Why javac requires an interfaces of referenced classes when ECJ does not?
编译 Client
时,它使用接口 I
的某些实现(例如 O
),I
的类文件也必须存在于类路径中。奇怪的是,这只是 javac
的情况,因为 Eclipse 编译器 (ECJ) 不需要 I
进行编译。
是什么使得 JDK 需要 超类型进行编译,而 ECJ 编译就很好?
这不是默认方法,如评论in the bug report, and the 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 也会针对 doit()
的定义达到 I
接口(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
如果 O
没有覆盖 doit()
怎么办?
那么 Client
必须仍然能够调用 doit()
,因为它是 I
合同的一部分,但是 O
的 class-文件.
你可能会问 "Why not include the default-methods definition in O
's class-file?"。这首先会破坏引入默认方法的意图:类 使用 pre-Java-8 编译器编译应该仍然可以在 Java8 中工作并且接口新方法应该可用.
似乎对 Compatibility Guide for JDK 8 的目的有误解。
这不是关于编译器或环境应该如何行为的规范,它是关于JDK如何的文档] 行为,以发现潜在的兼容性问题。这并不意味着另一个编译器必须表现出完全相同的行为。
之所以提到该特定行为,是因为 javac
将其行为从 JDK 7 更改为 JDK 8,这可能会导致兼容性问题。
如here所述,正式过程被描述为为一个方法调用搜索所有可能适用的成员方法,但它并没有说当一个方法的正确性时不允许走捷径程序可以保证。
所以 that bug report 已经关闭,因为新行为符合规范,不一定是因为替代行为会违反规范。
编译 Client
时,它使用接口 I
的某些实现(例如 O
),I
的类文件也必须存在于类路径中。奇怪的是,这只是 javac
的情况,因为 Eclipse 编译器 (ECJ) 不需要 I
进行编译。
是什么使得 JDK 需要 超类型进行编译,而 ECJ 编译就很好?
这不是默认方法,如评论in the bug report, and the 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 也会针对doit()
的定义达到
I
接口(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
如果 O
没有覆盖 doit()
怎么办?
那么 Client
必须仍然能够调用 doit()
,因为它是 I
合同的一部分,但是 O
的 class-文件.
你可能会问 "Why not include the default-methods definition in O
's class-file?"。这首先会破坏引入默认方法的意图:类 使用 pre-Java-8 编译器编译应该仍然可以在 Java8 中工作并且接口新方法应该可用.
似乎对 Compatibility Guide for JDK 8 的目的有误解。
这不是关于编译器或环境应该如何行为的规范,它是关于JDK如何的文档] 行为,以发现潜在的兼容性问题。这并不意味着另一个编译器必须表现出完全相同的行为。
之所以提到该特定行为,是因为 javac
将其行为从 JDK 7 更改为 JDK 8,这可能会导致兼容性问题。
如here所述,正式过程被描述为为一个方法调用搜索所有可能适用的成员方法,但它并没有说当一个方法的正确性时不允许走捷径程序可以保证。
所以 that bug report 已经关闭,因为新行为符合规范,不一定是因为替代行为会违反规范。