I have a class with 2 methods.
In
method1()
, I create alocal record
calledAbc
. Thislocal record
is only available tomethod1()
because it was defined inmethod1()
(here are the rules on it according to Java).In
method2()
, I create alocal interface
calledABC
. Notice that there is a capitalization difference here. Thelocal record
was namedAbc
, but thislocal interface
is namedABC
.
Here is the class.
//package ..... //commenting out package information, as I sincerely doubt that is the cause
/** There seems to be a class loader error when running the below method in main(). */
public class ClassLoaderIssue
{
/** Method 1. */
private void method1()
{
record Abc(int a)
{
public static String iDontCare()
{
return "ignore this method";
}
}
System.out.println(Abc.iDontCare()); //error
}
/** Method 2. */
private void method2()
{
interface ABC
{
}
}
/**
*
* Main method.
*
* @param args commandline arguments that we don't care about for this example.
*
*/
public static void main(String[] args)
{
new ClassLoaderIssue().method1();
}
}
And here is the Exception that I get.
Exception in thread "main" java.lang.NoClassDefFoundError: ClassLoaderIssue$1ABC (wrong name: ClassLoaderIssue$1Abc)
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1012)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150)
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862)
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
at ClassLoaderIssue.method1(ClassLoaderIssue.java:23)
at ClassLoaderIssue.main(ClassLoaderIssue.java:49)
And here is some relevant information.
Java compile command = C:\Program Files\Java\jdk-17.0.2\bin\javac.exe
Java run command = C:\Program Files\Java\jdk-17.0.2\bin\java.exe
Obviously, this is just a runnable example of the problem, but I have a situation where it is extremely convenient and helpful to have local class/enum/record
to handle weird state at the very edges of my domain. Therefore, I use this pattern a lot, but this is the first time I have come across this issue.
Now obviously, there are many ways to go around this. The simplest being that I could just rename the enum for one. I already tried that and that worked. Same for the interface.
But why does this happen?
EDIT - I also tested this with Java 18 and got the same result. I used a brand new file, a brand new folder, and I copied and pasted the text (not the file), into an empty file with the same name. Same error. I can't really get a cleaner test than that.
On Windows, file names are case-insensitive. This means that files called Something$ABC.class and Something$Abc.class cannot be distinguished.
Meanwhile, of course, the language itself is case-sensitive, and expects the file name and the name of the class within the file to match.
Your only recourse seems to be to rename either the class or the interface.