Accessing Package Private element between parent / child classloaders

classloader, java

Solution

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

Problem

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.

Original source