I can’t believe that I have to ask this question, it’s really confusing. The purpose of this question is to find out why and / or find a simpler way (than reflection) to get the correct result.
Background story
I downloaded some class files from directories and jar files through URLClassLoader, and I would like to reset all class names and their declared methods. Classes cannot be initialized (see false), because if any code is executed in these classes, exceptions may be thrown due to some missing dependencies. I ran into this problem trying to print only class names.
Question
What am I missing, how can I get around Groovy magic and just call a method (called getName) on an object (type java.lang.Class) without reflection? Someone will also point out a specification as to why this works this way.
Here is the output for my mini-test (see below) and my comments:
.name
name and Y
.getName()
X and thrown SHOULD NOT INIT
["name"]
name and thrown SHOULD NOT INIT
reflection
X and Y
Wiring harness and control samples
Changing the test closure, which should be explicit for the argument ( Class<?> c ->) type, does not matter.
new File("X.java").write('''
public class X {
public static String get(String key) {
return key;
}
}
''');
new File("Y.java").write('''
public class Y {
static {
if (true) // needed to prevent compile error
throw new UnsupportedOperationException("SHOULD NOT INIT");
}
}
''');
print 'javac X.java Y.java'.execute().err.text;
def test = { String title, Closure nameOf ->
URL url = new File(".").toURI().toURL();
// need a new ClassLoader each time because it remembers
// if a class has already thrown ExceptionInInitializerError
ClassLoader loader = java.net.URLClassLoader.newInstance(url);
// false means not to initialize the class.
// To get the name of the class there no need to init
// as shown in the reflection test.
// Even fields and methds can be read without initializing,
// it essentially just parsing the .class file.
Class x = Class.forName("X", false, loader);
Class y = Class.forName("Y", false, loader);
println()
println title
try {
print nameOf(x)
} catch (Throwable ex) {
print "thrown " + ex.cause?.message
}
print " and "
try {
print nameOf(y)
} catch (Throwable ex) {
print "thrown " + ex.cause?.message
}
println()
}
test '.name', { c -> c.name; }
test '.getName()', { c -> c.getName(); }
test '["name"]', { c -> c["name"] }
test 'reflection', { c -> java.lang.Class.class.getDeclaredMethod("getName").invoke(c); }
source
share