In a Java application, I have two classes declared, one class (One
) declared in ClassLoader A and another class (Two
) declared in ClassLoader B. ClassLoader A is B's parent. Both of these classes have the same package (ie: org.test
).
I cannot seem to access One
's package private methods or varialbes from Two
event though A is B'd parent ClassLoader, I get an IllegalAccessError
exception. I understand that package private accessibility is based both on the package name and the ClassLoader.
Is there a way to re-associate One
and Two
so that Two
can access One
's Package Private elements?
Here's the test to demonstrate this:
package org.test;
public class ClassLoaderTest {
@Test
public void testLoading() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
One one = new One();
one.value = "test";
MemoryClassLoader classLoader = new MemoryClassLoader();
String name = one.getClass().getPackage().getName() + ".Two";
classLoader.add(name,
"package org.test;\n" +
"\n" +
"public class Two{\n" +
" public static String getValue(One one){\n" +
" return one.value;\n" +
" }\n" +
"}");
Class<?> twoClass = classLoader.loadClass(name);
assertEquals("test", twoClass.getMethod("getValue", One.class).invoke(null, one));
}
}
public class One{
String value;
}
MemoryClassLoader can be found here.
which errors with:
testLoading(org.test.ClassLoaderTest) Time elapsed: 0.214 sec <<< ERROR!
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.test.ClassLoaderTest.testLoading(ClassLoaderTest.java:37)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
....
Caused by: java.lang.IllegalAccessError: tried to access field org.test.One.value from class org.test.Two
at org.test.Two.getValue(Two.java:5)
... 34 more
Thanks.
Edit:
I've produced a Gist with a self contained test demonstrating this here.
You cannot call a private method of a class from any other class(be it the extending class). It can only be accessed inside that class itself. There is a reason it is said to be private .
In effect, a classloader is responsible for loading only the classes not available to the parent. The exception is the web application classloader, which follows the delegation model in the Servlet specification. The web application Classloader looks in the local classloader before delegating to its parent.
A class loader is an object that is responsible for loading classes. The class ClassLoader is an abstract class. Given the binary name of a class, a class loader should attempt to locate or generate data that constitutes a definition for the class.
Load class with forName() method in Java The class object associated with the class with the given string name can be returned with the method java. lang. Class. forName(String name, boolean initialize, ClassLoader loader), using the class loader that is used to load the class.
Is there a way to re-associate One and Two so that Two can access One's Package Private elements?
Simple Answer: With Reflection: YES; Without Reflection: NO.
Detailed Answer: Java has its own mechanisms to control the access, prevention of users of a package or class from depending on unnecessary details of the implementation of that package or class. If access is permitted, then the accessed entity is said to be accessible. Accessibility is a static property that can be determined at compile time; it depends only on types and declaration modifiers.
From Java SE specs
If the member or constructor is declared private, then access is permitted if and only if it occurs within the body of the top level class that encloses the declaration of the member or constructor. A private class member or constructor is accessible only within the body of the top level class that encloses the declaration of the member or constructor. It is not inherited by subclasses.
Hence, it is not possible in Java SE without using Reflection API.
If Reflection API is used, then the static introspection of accessibility is changed to dynamic. Classes are loaded dynamically, binding is done dynamically, and object instances are created dynamically on the fly when they are needed. What has not been very dynamic historically is the ability to manipulate "anonymous" classes. In this context, an anonymous class is one that is loaded or presented to a Java class at run time and whose type was previously unknown to the Java program.
Snippet of class & method access code using Reflection API
Object o = hi.getClass().getMethod("foo").invoke(hi);
Method m = o.getClass().getMethod("bar");
m.setAccessible(true);
m.invoke(o);
Take an in-depth look at the Java Reflection API.
It is not always necessary to prove that impossible itself says I M Possible. In this case Reflection API has reinvented the wheel for us, we shouldn't be looking to reinvent it all together again from scratch. (on a lighter note, nothing to offense.)
Shishir
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With