Using the qualified name of a subclass compiled by gen-class
to access the superclass' members throws an exception. Using the simple name proceeds as expected.
Here's an example that I've tried to cut down as much as possible, but it's still lengthy. All files are relative to the project root directory, indicated as <project root>
.
Define a class in file <project root>/src/com/example/SomeClass.java
with two members, a static field and a static method.
package com.example;
public class SomeClass {
public static int aField = 99;
public static String aStaticMethod(int i) {
return "aStaticMethod received " + i;
}
}
Define a subclass in file <project root>/src/com/example/SomeSubClass.java
that extends the previous class.
package com.example;
public class SomeSubClass extends SomeClass {
}
Compile with
$ javac -d target/classes/ src/com/example/SomeClass.java
$ javac -d target/classes/ -cp target/classes/ src/com/example/SomeSubClass.java
Now we have files
<project root>/target/classes/com/example/SomeClass.class
<project root>/target/classes/com/example/SomeSubClass.class
Let's see how Clojure handles those two classes. In file <project root>/src/com/example2/demo.clj
(ns demo)
;; javac-compiled SuperClass
(import com.example.SomeClass)
(com.example.SomeClass/aField) ;; 99
(com.example.SomeClass/aStaticMethod 123) ;; "aStaticMethod received 123"
;; javac-compiled SubClass
(import com.example.SomeSubClass)
(com.example.SomeSubClass/aField) ;; 99
(com.example.SomeSubClass/aStaticMethod 456) ;; "aStaticMethod received 456"
All fine and good: Clojure can resolve the superclass members aField
and aStaticMethod
by using the qualified name com.example.SomeSubClass
.
Now, in file <project root>/src/com/example/ExtendSomeClass.clj
, define another subclass --- ExtendSomeClass
--- this time using Clojure's gen-class
.
(ns com.example.ExtendSomeClass)
(gen-class
:name com.example.ExtendSomeClass
:extends com.example.SomeClass)
Back in file <project root>/src/com/example2/demo.clj
, compile ExtendSomeClass
by evaluating
(compile 'com.example.ExtendSomeClass)
which produces in directory <project root>/target/classes/com/example/
the following four files
ExtendSomeClass.class
ExtendSomeClass.class$fn__NNNN.class
ExtendSomeClass.class$loading__NNNN__auto__NNNN.class
ExtendSomeClass__init.class
I would expect the behavior of the gen-class
-compiled ExtendSomeClass to be substantially the same as the javac-compiled SomeSubClass. However, I obtain an exception when I attempt to access the static members inherited from the superclass with the qualified classname.
Returning to file <project root>/src/com/example2/demo.clj
(import com.example.ExtendSomeClass)
(com.example.ExtendSomeClass/aField)
;; java.lang.RuntimeException
;; No such var: com.example.ExtendSomeClass/aField
(com.example.ExtendSomeClass/aStaticMethod 789)
;; java.lang.RuntimeException
;; No such var: com.example.ExtendSomeClass/aStaticMethod
Interestingly, using the unqualified classname works as expected.
(ExtendSomeClass/aField) ;; 99
(ExtendSomeClass/aStaticMethod 789) ;; "aStaticMethod received 789"
Q: How can I understand the difference in the behavior of a gen-class
-compiled subclass and the javac-compiled subclass. Should I double-check how the classpaths align to the directory structure?
Appendix:
I disassembled the class files and observed the following:
Compiled from "SomeClass.java"
public class com.example.SomeClass ...
Compiled from "SomeSubClass.java"
public class com.example.SomeSubClass extends com.example.SomeClass ...
public class com.example.ExtendSomeClass extends com.example.SomeClass ...
(This last result is missing the "Compiled from ___.java" first line.)
And when I make a demonstration to see how the gen-class
-compiled class works from java-land:
package com.example;
import com.example.ExtendSomeClass;
public class TestExtendSomeClass {
public static void main(String[] args) {
System.out.println(com.example.ExtendSomeClass.aStaticMethod(123));
}
}
I am able to access the superclass fields with the qualified class name.
$ java -cp target/classes/ com.example.TestExtendSomeClass
aStaticMethod received 123