I come across to a strange behavior while trying to override a method with default accessor (ex: void run()
).
According to Java spec, a class can use or override default members of base class if classes belongs to the same package.
Everything works correctly while all classes loaded from the same classloader.
But if I try to load a subclass from separate classloader then polymorphism don't work.
Here is sample:
App.java:
import java.net.*;
import java.lang.reflect.Method;
public class App {
public static class Base {
void run() {
System.out.println("error");
}
}
public static class Inside extends Base {
@Override
void run() {
System.out.println("ok. inside");
}
}
public static void main(String[] args) throws Exception {
{
Base p = (Base) Class.forName(Inside.class.getName()).newInstance();
System.out.println(p.getClass());
p.run();
} {
// path to Outside.class
URL[] url = { new URL("file:/home/mart/workspace6/test2/bin/") };
URLClassLoader ucl = URLClassLoader.newInstance(url);
final Base p = (Base) ucl.loadClass("Outside").newInstance();
System.out.println(p.getClass());
p.run();
// try reflection
Method m = p.getClass().getDeclaredMethod("run");
m.setAccessible(true);
m.invoke(p);
}
}
}
Outside.java: should be in separate folder. otherwise classloader will be the same
public class Outside extends App.Base {
@Override
void run() {
System.out.println("ok. outside");
}
}
The output:
class App$Inside
ok. inside
class Outside
error
ok. outside
So then I call Outside#run()
I got Base#run()
("error" in output). Reflections works correctly.
Whats wrong? Or is it expected behavior? Can I go around this problem somehow?
From Java Virtual Machine Specification:
5.3 Creation and Loading
...
At run time, a class or interface is determined not by its name alone, but by a pair: its fully qualified name and its defining class loader. Each such class or interface belongs to a single runtime package. The runtime package of a class or interface is determined by the package name and defining class loader of the class or interface.
5.4.4 Access Control
...
A field or method R is accessible to a class or interface D if and only if any of the following conditions is true:
- ...
- R is either
protected
or package private (that is, neitherpublic
norprotected
norprivate
), and is declared by a class in the same runtime package as D.