Accessing non-visible classes with reflection

JavaClassReflectionPackage Private

Java Problem Overview


I am trying to get an instance of a non-visible class, AKA package private class, using reflection. I was wondering if there was a way to switch the modifiers to make it public and then access it using Class.forName. When I try that now it stops me saying I can't do it. Unfortunately there is no setAccesible method of the Class class.

Java Solutions


Solution 1 - Java

nested class - class defined within other class (includes static and non-static classes)
inner class - non-static nested class (instance of inner class need instance of outer class to exist)

non-nested (top level) classes

Based on your question we know that constructor you want to access is not public. So your class may look like this (A class is in some package different than ours)

package package1;

public class A {
	A(){
		System.out.println("this is non-public constructor");
	}
}

To create instance of this class we need to get to constructor we want to invoke, and make it accessible. When it is done we can use Constructor#newInstance(arguments) to create instance.

Class<?> c = Class.forName("package1.A");
//full package name --------^^^^^^^^^^
//or simpler without Class.forName:
//Class<package1.A> c = package1.A.class;

//In our case we need to use
Constructor<?> constructor = c.getDeclaredConstructor();
//note: getConstructor() can return only public constructors
//so we needed to search for any Declared constructor

//now we need to make this constructor accessible
constructor.setAccessible(true);//ABRACADABRA!

Object o = constructor.newInstance();

nested and inner classes

If you want to access nested (static and non-static) Class with Class.forName you need to use syntax:

Class<?> clazz = Class.forName("package1.Outer$Nested");

Outer$Nested says that Nested class is declared within Outer class. Nested classes are very similar to methods, they have access to all members of its outer class (including private ones).

But we need to remember that instance of inner class to exists requires instance of its outer class. Normally we create them via:

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();

so as you see each instance of Inner class have some information about its outer class (reference to that outer instance is stored in this$0 field, more info: https://stackoverflow.com/questions/28462849/what-does-it-mean-if-a-variable-has-the-name-this0-in-intellij-idea-while-deb/28462949#28462949)

So while creating instance of Inner class with Constructor#newInstance() you need to pass as first argument reference to instance of Outer class (to simulate outer.new Inner() behavior).

Here is an example.

in package1

package package1;

public class Outer {
	class Inner{
		Inner(){
			System.out.println("non-public constructor of inner class");
		}
	}
}

in package2

package package2;

import package1.Outer;
import java.lang.reflect.Constructor;

public class Test {
	public static void main(String[] args) throws Exception {

		Outer outerObject = new Outer();

		Class<?> innerClazz = Class.forName("package1.Outer$Inner");

		// constructor of inner class as first argument need instance of
		// Outer class, so we need to select such constructor
		Constructor<?> constructor = innerClazz.getDeclaredConstructor(Outer.class);

		//we need to make constructor accessible 
		constructor.setAccessible(true);

		//and pass instance of Outer class as first argument
		Object o = constructor.newInstance(outerObject);
		
		System.out.println("we created object of class: "+o.getClass().getName());

	}
}

static-nested classes

Instances of static-nested classes don't require instance of Outer class (since they are static). So in their case we don't need to look for constructor with Outer.class as first argument. And we don't need to pass instance of outer class as first argument. In other words code will be same as for non-nested (top-level) class (maybe except the fact that you would need to add use $ instead of . in class name to when referring to nested classes in Class.forName() like Class.forName("some.package.Outer$Nested1$NestedNested")).

Solution 2 - Java

Class.forName should work. If the class is within a package hierarchy list in the "package.access" security property, then you will need to perform the operation with the appropriate privilege (usually all permissions; or don't have a security manager).

If you are trying to use Class.newInstance, don't. Class.newInstance handles exceptions poorly. Instead get a Constructor and call newInstance on that. It's difficult to see what you are having problems with without the exception trace.

As ever, most but not all uses of reflection are bad ideas.

Solution 3 - Java

We recently released a library that helps a lot to access private fields, methods and inner classes through reflection : BoundBox

For a class like

public class Outer {
    private static class Inner {
        private int foo() {return 2;}
    }
}

It provides a syntax like :

Outer outer = new Outer();
Object inner = BoundBoxOfOuter.boundBox_new_Inner();
new BoundBoxOfOuter.BoundBoxOfInner(inner).foo();

The only thing you have to do to create the BoundBox class is to write @BoundBox(boundClass=Outer.class) and the BoundBoxOfOuter class will be instantly generated.

Solution 4 - Java

I had requirement to copy the value of field from older version of object if the value is null in latest version. We had these 2 options.

Core Java :

for (Field f : object.getClass().getSuperclass().getDeclaredFields()) {
    f.setAccessible(true);
  System.out.println(f.getName());
  if (f.get(object) == null){
    f.set(object, f.get(oldObject));
  }
}

Using Spring [org.springframework.beans.BeanWrapper] :

BeanWrapper bw = new BeanWrapperImpl(object);
PropertyDescriptor[] data = bw.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : data) {
  System.out.println(propertyDescriptor.getName());
  Object propertyValue = bw.getPropertyValue(propertyDescriptor.getName());
  if(propertyValue == null )
    bw.setPropertyValue( new PropertyValue(propertyDescriptor.getName(),"newValue"));
}
    	

Solution 5 - Java

Had to do something similar on Android here is what I came up with:

/**
 * To fix issues with wrong edge-to-edge bottom sheet top padding and status bar icon color…
 * …we need to call [BottomSheetDialog.EdgeToEdgeCallback.setPaddingForPosition] which is a private function from a private class.
 * See: https://github.com/material-components/material-components-android/issues/2165
 */
fun adjustBottomSheet(aDialog : BottomSheetDialog) {
    // Get our private class
    val classEdgeToEdgeCallback = Class.forName("com.google.android.material.bottomsheet.BottomSheetDialog\$EdgeToEdgeCallback")
    // Get our private method
    val methodSetPaddingForPosition: Method = classEdgeToEdgeCallback.getDeclaredMethod("setPaddingForPosition", View::class.java)
    methodSetPaddingForPosition.isAccessible = true
    // Get private field containing our EdgeToEdgeCallback instance
    val fieldEdgeToEdgeCallback = BottomSheetDialog::class.java.getDeclaredField("edgeToEdgeCallback")
    fieldEdgeToEdgeCallback.isAccessible = true
    // Get our bottom sheet view field
    val fieldBottomField = BottomSheetDialog::class.java.getDeclaredField("bottomSheet")
    fieldBottomField.isAccessible = true
    // Eventually call setPaddingForPosition from EdgeToEdgeCallback instance passing bottom sheet view as parameter
    methodSetPaddingForPosition.invoke(fieldEdgeToEdgeCallback.get(aDialog),fieldBottomField.get(aDialog))
}

Also you may want to make sure proguard does not discard the method you are trying to access:

# Needed for now to fix our bottom sheet issue from 
com.google.android.material:material:1.4.0-alpha02
-keep class com.google.android.material.bottomsheet.BottomSheetDialog$EdgeToEdgeCallback {
    private void setPaddingForPosition(android.view.View);
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJosh SobelView Question on Stackoverflow
Solution 1 - JavaPshemoView Answer on Stackoverflow
Solution 2 - JavaTom Hawtin - tacklineView Answer on Stackoverflow
Solution 3 - JavaSnicolasView Answer on Stackoverflow
Solution 4 - JavaVik_TechnologistView Answer on Stackoverflow
Solution 5 - JavaSlionView Answer on Stackoverflow