If one writes two public Java classes with the same case-insensitive name in different directories then both classes are not usable at runtime. (I tested this on Windows, Mac and Linux with several versions of the HotSpot JVM. I would not be surprised if there other JVMs where they are usable simultaneously.) For example, if I create a class named a
and one named A
like so:
// lowercase/src/testcase/a.java
package testcase;
public class a {
public static String myCase() {
return "lower";
}
}
// uppercase/src/testcase/A.java
package testcase;
public class A {
public static String myCase() {
return "upper";
}
}
Three eclipse projects containing the code above are available from my website.
If try I calling myCase
on both classes like so:
System.out.println(A.myCase());
System.out.println(a.myCase());
The typechecker succeeds, but when I run the class file generate by the code directly above I get:
Exception in thread "main" java.lang.NoClassDefFoundError: testcase/A (wrong name: testcase/a)
In Java, names are in general case sensitive. Some file systems (e.g. Windows) are case insensitive, so I'm not surprised the above behavior happens, but it seems wrong. Unfortunately the Java specifications are oddly non-commital about which classes are visible. The Java Language Specification (JLS), Java SE 7 Edition (Section 6.6.1, page 166) says:
If a class or interface type is declared public, then it may be accessed by any code, provided that the compilation unit (§7.3) in which it is declared is observable.
In Section 7.3, the JLS defines observability of a compilation unit in extremely vague terms:
All the compilation units of the predefined package java and its subpackages lang and io are always observable. For all other packages, the host system determines which compilation units are observable.
The Java Virtual Machine Specification is similarly vague (Section 5.3.1):
The following steps are used to load and thereby create the nonarray class or interface C denoted by [binary name] N using the bootstrap class loader [...] Otherwise, the Java virtual machine passes the argument N to an invocation of a method on the bootstrap class loader to search for a purported representation of C in a platform-dependent manner.
All of this leads to four questions in descending order of importance:
- Are there any guarantees about which classes are loadable by the default class loader(s) in every JVM? In other words, can I implement a valid, but degenerate JVM, that won't load any classes except those in java.lang and java.io?
- If there are any guarantees, does the behavior in the example above violate the guarantee (i.e. is the behavior a bug)?
- Is there any way to make HotSpot load
a
andA
simultaneously? Would writing a custom class loader work?
testcase.a
andtestcase.A
, in two different directories on your classpath (because you can't have them in the same directory on a case insensitive filesystem) - and you're wondering why the JVM can't find the correct class file to load? – Greg Hewgilla
andA
simultaneously. Just place them in a JAR file and load from there. – Hot Licks