3

I have a class with 2 methods.

  • In method1(), I create a local record called Abc. This local record is only available to method1() because it was defined in method1() (here are the rules on it according to Java).

  • In method2(), I create a local interface called ABC. Notice that there is a capitalization difference here. The local record was named Abc, but this local interface is named ABC.

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.

davidalayachew
  • 1,279
  • 1
  • 11
  • 22

2 Answers2

5

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.

dangling else
  • 438
  • 2
  • 3
  • Great catch, ty again. Question though - would this not be considered a bug that I should submit to Oracle? I feel like this should be addressed, because it very much sounds like it is attempting to create both a `ABC` and `Abc` class file. More importantly, this hurts portability, and the solution is really simple (just increment that `$1` to be `$2`) – davidalayachew May 30 '22 at 18:37
  • 1
    You could try reporting it - I'm not sure how responsive they are. – dangling else May 30 '22 at 19:16
  • (@davidalayachew) You _could_ compile in WSL, or more generally any Unix VM, or on a (different) real Unix machine, put in a jar and run from the jar on Windows; _jar_ entries are case-sensitive even when the 'real' (OS) filesystem is not. Less convenient though. – dave_thompson_085 May 30 '22 at 19:23
  • Fun fact: NTFS actually can be operated case-sensitive. But you **really** don't want to do that. – dangling else May 30 '22 at 19:26
  • @danglingelse I will do exactly that then, ty! I will post updates to the bug here. – davidalayachew May 30 '22 at 19:53
  • @dave_thompson_085 thank you, I will use that as a workaround until they patch it – davidalayachew May 30 '22 at 19:53
  • Ok, the bug has been submitted! Here is the link -- https://bugs.openjdk.org/browse/JDK-8287885 – davidalayachew Jun 09 '22 at 04:08
  • Well unfortunately, it seems that the developer doesn't agree, and thinks that this is only an enhancement. I have tried to appeal the decision, but it seems like they are not budging. Hopefully they change their mind. – davidalayachew Jul 20 '22 at 08:42
  • As a follow up update, I started a discussion on this on the java mailing list here. A good faith effort was made by the compiler-dev folks, but it looks like this is an extremely complex situation to navigate out of. It wasn't shut down, but it doesn't look like any progress has been made for a few weeks now. Likely won't for a while either. – davidalayachew Mar 23 '23 at 06:37
  • Here is the discussion -- https://mail.openjdk.org/pipermail/compiler-dev/2023-February/022222.html – davidalayachew Mar 23 '23 at 06:37
  • I forgot to give an update - Wonderful news! Archie Cobbs was able to create an opt-in warning that catches and deals with this issue. Here is a demo of me using it on the preview version of JDK 21 -- https://mail.openjdk.org/pipermail/compiler-dev/2023-April/022930.html -- Thank you all for your help in improving Java! – davidalayachew May 29 '23 at 05:23
  • I should also add - I built the JDK myself when doing this, but all of the prebuilt beta binaries should have this ready to go. As I said, I was late in giving this update. There have been several beta builds since. – davidalayachew May 29 '23 at 05:25
0

I just compiled the code and ran it on my machine. It works without any problems.

Maybe try to delete all existing class files and recompile everything.

Otherwise you can try a different java version, I had similar problems related to that in the past.