我遇到了一个奇怪的JLS或JavaC错误(不确定是哪个)。请阅读以下内容并提供解释,并酌情引用JLS段落或Sun Bug ID。
假设我有一个人为的项目,其代码包含三个“模块” -
以下是每个模块中的类:
API - MessagePrinter.java
package api;
public class MessagePrinter {
public void print(String message) {
System.out.println("MESSAGE: " + message);
}
}
API - MessageHolder.java
(是的,它引用了一个“impl”类 - 稍后会详细介绍)
package api;
import impl.MessagePrinterInternal;
public class MessageHolder {
private final String message;
public MessageHolder(String message) {
this.message = message;
}
public void print(MessagePrinter printer) {
printer.print(message);
}
/**
* NOTE: Package-Private visibility.
*/
void print(MessagePrinterInternal printer) {
printer.print(message);
}
}
Impl - MessagePrinterInternal.java
- 此类依赖于API类。顾名思义,它用于我的小框架中的“内部”使用。
package impl;
import api.MessagePrinter;
/**
* An "internal" class, not meant to be added to your
* application classpath. Think the Tomcat Servlet API implementation classes.
*/
public class MessagePrinterInternal extends MessagePrinter {
public void print(String message) {
System.out.println("INTERNAL: " + message);
}
}
最后,App模块中唯一的类...... MyApp.java
import api.MessageHolder;
import api.MessagePrinter;
public class MyApp {
public static void main(String[] args) {
MessageHolder holder = new MessageHolder("Hope this compiles");
holder.print(new MessagePrinter());
}
}
所以,现在我尝试编译我的小应用程序MyApp.java。假设我的API罐子是通过jar导出的,例如api.jar,并且是一个好公民我只在我的类路径中引用那个jar - 而不是impl.jar中的Impl类。
现在,我的框架设计中存在一个缺陷,即API类不应该依赖于“内部”实现类。然而,令人惊讶的是MyApp.java根本没有编译。
javac -cp api.jar src\MyApp.java
src\MyApp.java:11: cannot access impl.MessagePrinterInternal class file for impl.MessagePrinterInternal not found
holder.print(new MessagePrinter());
^
1 error
问题是由于方法重载,编译器正在尝试解析要使用的版本print()。但是,编译错误有些出乎意料,因为其中一个方法是包私有的,因此对MyApp不可见。
那么,这是一个javac错误,还是JLS的一些奇怪之处?
编译:Sun javac 1.6.0_14
答案 0 :(得分:1)
JLS或javac没有任何问题。当然这不会编译,因为如果我理解你的解释正确,你的类MessageHolder
引用MessagePrinterInternal
不在编译类路径上。您必须将此引用分解为实现,例如使用API中的接口。
编辑1:澄清:这与您认为的包可见方法无关。问题是编译需要类型MessagePrinterInternal
,但是类路径上没有它。当javac无法访问引用的类时,你不能指望它能够编译源代码。
答案 1 :(得分:0)
首先,我希望api包中的东西是接口而不是类(基于名称)。一旦你这样做,问题就会消失,因为你不能在接口中拥有包访问权。
接下来就是,AFAIK,这是一个Java奇怪的东西(因为它不会做你想要的)。如果你摆脱了公共方法并将软件包私有化,你就会得到同样的东西。
将api包中的所有内容更改为接口将解决您的问题并使您的代码更清晰。
答案 2 :(得分:0)
我想你总是可以争辩说javac可以更聪明,但它必须停在某个地方。它不是人类,人类可以永远比编译器更聪明,你总能找到对人类完全有意义的例子但却让人傻眼的编译器。
我不知道这个问题的具体规格,我怀疑javac作者在这里犯了什么错误。但谁在乎?为什么不将所有依赖项放在类路径中,即使它们中的一些是肤浅的?这样做可以让我们的生活更轻松。