有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

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

是什么使得JDK需要超类型进行编译,而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()

接口(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) 个答案

  1. # 1 楼答案

    关于Compatibility Guide for JDK 8的目的似乎存在误解

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

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

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

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

  2. # 2 楼答案

    如果O不覆盖doit()怎么办

    然后Client必须仍然能够调用doit(),因为它是I合同的一部分,但是O的类文件中缺少此信息

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