Let's use reflection Private method access

WithOne AdventCalendar This is the article on the 13th day.

In this article, the "java.lang.reflect" package. Introducing so-called reflection.

This time I will write about "prirvate" methods and how to access fields. It's easy if you know the reflection, but "prirvate = absolutely inaccessible from the outside." It seems that there are quite a lot of people who think that.

If you do not know it, it will affect the progress of Junit test etc., so I will explain the method roughly.

Let's use it for the time being

The following is the class received as an argument and the class that executes the method.

TestMain.java


public class TestMain {

	/**
	 *Main class<br>
	 *Main to do something
	 * @param args argument
	 */
	public static void main(String[] args) {
		System.out.println("Start TestMain");

		//The first argument is the class name
		String className = args[0];

		//The second argument is the method name
		String methodName = args[1];

		try {

			//Get class
			Class<?> c = Class.forName(className);
			System.out.println("Execution class:" + c.getName());

			//Create an instance
			Object o = c.newInstance();

			//Get method
			Method m = c.getMethod(methodName);
			System.out.println("Execution method:" + m.getName());

			//Run
			m.invoke(o);

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("Finish TestMain");
		}
	}
}

Well, if you look inside the framework, it's a process that seems to be well written. When writing the class name in the property file, it is OK if you think that the process like this is being executed internally.

Let's run it now.

image

If you set the class and method you want to execute in the argument and execute ...

image

You can see that TestExecute.execute is being executed.

But something is wrong. Let's take a look at TestExecute.

TestExecute.java


public class TestExecute {

	/**Execution flag*/
	private static boolean FLG = true;

	/**
	 *Execution method
	 */
	public void execute() {
		System.out.println("Start TestExecute.execute");

		try {
			//No matter how hard you try
			if (FLG) {
				throw new Exception();
			}

			//Call part of the process you want to execute
			prirvateMethod();

		}catch (Exception e) {
			System.out.println("Failure occurred!");
		} finally {
			System.out.println("Finish TestExecute.execute");
		}
	}

	/**
	 *Private method
	 */
	private void prirvateMethod() {
		//The process you want to execute
		System.out.println("Execute TestExecute.prirvateMethod");
	}
}

Since the private field "FLG" is "true", it will definitely fail. Moreover, the process that you actually want to move as a comment seems to be "prirvateMethod ()".

Let's run that

image

Run!

image

You got angry, "There is no such method." Well, it's private. Private methods are usually invisible to the outside world and cannot be accessed.

Let's rewrite the information "inaccessible"

However, since "java.lang.reflect.Method" is a class for controlling methods, access control can be changed as a matter of course. To access private, change the source as follows.

TestMain.java


public class TestMain {

	/**
	 *Main class<br>
	 *Main to do something
	 *
	 * @param args argument
	 */
	public static void main(String[] args) {
		System.out.println("Start TestMain");

		//The first argument is the class name
		String className = args[0];

		//The second argument is the method name
		String methodName = args[1];

		try {

			//Get class
			Class<?> c = Class.forName(className);
			System.out.println("Execution class:" + c.getName());

			//Create an instance
			Object o = c.newInstance();

			//Get method
			Method m = c.getDeclaredMethod(methodName);
			System.out.println("Execution method:" + m.getName());

			//Change method accessible
			m.setAccessible(true);

			//Run
			m.invoke(o);

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("Finish TestMain");
		}
	}
}

"GetMethod" has been changed to "getDeclaredMethod" and a line "m.setAccessible (true)" has been added. Declared means that it has been declared, so if you write it for the time being, you can use it or not, but you will get it! It is a method called.

You can get it with "getDeclaredMethod", It cannot be executed just by getting it, and "java.lang.IllegalAccessException" occurs as shown below.

image

Therefore, use "setAccessible" to forcibly change it to an accessible state.

Let's actually execute it.

image

You can see that the "prirvate Method" is being executed.

Let's rewrite the private field

But after all I want you to execute "execute", so let's rewrite the field "FLG". The field "FLG" is private and normally inaccessible, but you can change it to be accessible in the same way as a method.

TestMain.java


public class TestMain {

	/**
	 *Main class<br>
	 *Main to do something
	 *
	 * @param args
	 *argument
	 */
	public static void main(String[] args) {
		System.out.println("Start TestMain");

		//The first argument is the class name
		String className = args[0];

		//The second argument is the method name
		String methodName = args[1];

		try {

			//Get class
			Class<?> c = Class.forName(className);
			System.out.println("Execution class:" + c.getName());

			//Create an instance
			Object o = c.newInstance();

			//Get field "FLG"
			Field f = c.getDeclaredField("FLG");
			System.out.println("Change field:" + f.getName());

			//access
			f.setAccessible(true);

			//Set value
			f.set(o, false);

			//Get method
			Method m = c.getMethod(methodName);
			System.out.println("Execution method:" + m.getName());

			//Run
			m.invoke(o);

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("Finish TestMain");
		}
	}
}

Run

image

image

You were able to execute the "prirvate Method" while executing the "execute".

That is all for the explanation of this reflection. Was it helpful?

Recommended Posts

Let's use reflection Private method access
private method
Let's use jcmd
[Swift] Let's use extension
[Swift] Let's use Segmented Control
[RSpec] Let's use FactoryBot [Rails]
[spring] Let's use Spring Data JPA
Let's use Swift Firebase Firebase Auth
Easy to use array.map (&: method)
Use Modifier # isStatic to determine if the [Reflection] method is static.