I've come across an oddity of the JLS, or a JavaC bug (not sure which). Please read the following and provide an explanation, citing JLS passage or Sun Bug ID, as appropriate.
Suppose I have a contrived project with code in three "modules" -
- API - defines the framework API - think Servlet API
- Impl - defines the API implementation - think Tomcat Servlet container
- App - the application I wrote
Here are the classes in each module:
API - MessagePrinter.java
package api;
public class MessagePrinter {
public void print(String message) {
System.out.println("MESSAGE: " + message);
}
}
API - MessageHolder.java
(yes, it references an "impl" class - more on this later)
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
- This class depends on an API class. As the name suggests, it is intended for "internal" use elsewhere in my little framework.
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);
}
}
Finally, the sole class in the App module...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());
}
}
So, now I attempt to compile my little application, MyApp.java. Suppose my API jars are exported via a jar, say api.jar, and being a good citizen I only referencd that jar in my classpath - not the Impl class shiped in impl.jar.
Now, obviously there is a flaw in my framework design in that the API classes shouldn't have any dependency on "internal" implementation classes. However, what came as a surprise is that MyApp.java didn't compile at all.
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
The problem is that the compiler is trying to resolve the version print() to use, due to method overloading. However, the compilation error is somewhat unexpected, as one of the methods is package-private, and therefore not visible to MyApp.
So, is this a javac bug, or some oddity of the JLS?
Compiler: Sun javac 1.6.0_14