CodeGym /Java Blog /Core Java /Reflection API: Reflection. The dark side of Java
Author
Pavlo Plynko
Java Developer at CodeGym

Reflection API: Reflection. The dark side of Java

Published in the Core Java group
Greetings, young Padawan. In this article, I'll tell you about the Force, a power that Java programmers only use in seemingly impossible situations. The dark side of Java is the Reflection API. In Java, reflection is implemented using the Java Reflection API.

What is Java reflection?

There is a short, accurate, and popular definition on the Internet. Reflection (from late Latin reflexio - to turn back) is a mechanism to explore data about a program while it is running. Reflection lets you explore information about fields, methods, and class constructors. Reflection lets you work with types that weren't present at compile time, but which became available during run time. Reflection and a logically consistent model for issuing error information make it possible to create correct dynamic code. In other words, an understanding how reflection works in Java will open up a number of amazing opportunities for you. You can literally juggle classes and their components. Here is a basic list of what reflection allows:
  • Learn/determine an object's class;
  • Get information about a class's modifiers, fields, methods, constants, constructors, and superclasses;
  • Find out what methods belong to implemented interface(s);
  • Create an instance of a class whose class name is unknown until run time;
  • Get and set values of an object's fields by name;
  • Call an object's method by name.
Reflection is used in almost all modern Java technologies. It's hard to imagine that Java, as a platform, could have achieved such widespread adoption without reflection. Most likely, it wouldn't have. Now that you're generally acquainted with reflection as a theoretical concept, let's proceed to its practical application! We won't learn all of the Reflection API's methods—just the ones that you'll actually encounter in practice. Since reflection involves working with classes, we'll start with a simple class called MyClass:

public class MyClass {
   private int number;
   private String name = "default";
//    public MyClass(int number, String name) {
//        this.number = number;
//        this.name = name;
//    }
   public int getNumber() {
       return number;
   }
   public void setNumber(int number) {
       this.number = number;
   }
   public void setName(String name) {
       this.name = name;
   }
   private void printData(){
       System.out.println(number + name);
   }
}
As you can see, this is a very basic class. The constructor with parameters is deliberately commented out. We'll come back to that later. If you looked carefully at the contents of the class, you probably noticed the absence of a getter for the name field. The name field itself is marked with the private access modifier: we can't access it outside of the class itself which means we can't retrieve its value. "So what's the problem?" you say. "Add a getter or change the access modifier". And you would be right, unless MyClass was in a compiled AAR library or in another private module with no ability to make changes. In practice, this happens all the time. And some careless programmer simply forgot to write a getter. This is the very time to remember reflection! Let's try to get to the private name field of the MyClass class:

public static void main(String[] args) {
   MyClass myClass = new MyClass();
   int number = myClass.getNumber();
   String name = null; // No getter =(
   System.out.println(number + name); // Output: 0null
   try {
       Field field = myClass.getClass().getDeclaredField("name");
       field.setAccessible(true);
       name = (String) field.get(myClass);
   } catch (NoSuchFieldException | IllegalAccessException e) {
       e.printStackTrace();
   }
   System.out.println(number + name); // Output: 0default
}
Let's analyze what just happened. In Java, there is a wonderful class called Class. It represents classes and interfaces in an executable Java application. We won't cover the relationship between Class and ClassLoader, since that isn't the topic of this article. Next, to retrieve this class's fields, you need to call the getFields() method. This method will return all of this class's accessible fields. This doesn't work for us, because our field is private, so we use the getDeclaredFields() method. This method also returns an array of class fields, but now it includes private and protected fields. In this case, we know the name of the field we're interested in, so we can use the getDeclaredField(String) method, where String is the desired field's name. Note: getFields() and getDeclaredFields() do not return the fields of a parent class! Great. We got a Field object referencing our name. Since the field was not public, we must grant access to work with it. The setAccessible(true) method lets us proceed further. Now the name field is under our complete control! You can retrieve its value by calling the Field object's get(Object) method, where Object is an instance of our MyClass class. We convert the type to String and assign the value to our name variable. If we can't find a setter to set a new value to the name field, you can use the set method:

field.set(myClass, (String) "new value");
Congratulations! You've just mastered the basics of reflection and accessed a private field! Pay attention to the try/catch block, and the types of exceptions being handled. The IDE will tell you their presence is required on its own, but you can clearly tell by their names why they are here. Moving on! As you might have noticed, our MyClass class already has a method for displaying information about class data:

private void printData(){
       System.out.println(number + name);
   }
But this programmer left his fingerprints here too. The method has a private access modifier, and we have to write our own code to display data every time. What a mess. Where'd our reflection go? Write the following function:

public static void printData(Object myClass){
   try {
       Method method = myClass.getClass().getDeclaredMethod("printData");
       method.setAccessible(true);
       method.invoke(myClass);
   } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
       e.printStackTrace();
   }
}
The procedure here is about the same as the one used for retrieving a field. We access the desired method by name and grant access to it. And on the Method object we call the invoke(Object, Args) method, where Object is also an instance of the MyClass class. Args are the method's arguments, though ours doesn't have any. Now we use the printData function to display information:

public static void main(String[] args) {
   MyClass myClass = new MyClass();
   int number = myClass.getNumber();
   String name = null; //?
   printData(myClass); // Output: 0default
   try {
       Field field = myClass.getClass().getDeclaredField("name");
       field.setAccessible(true);
       field.set(myClass, (String) "new value");
       name = (String) field.get(myClass);
   } catch (NoSuchFieldException | IllegalAccessException e) {
       e.printStackTrace();
   }
   printData(myClass);// Output: 0new value
}
Hurray! Now we have access to the class's private method. But what if the method does have arguments, and why is the constructor commented out? Everything in its own due time. It is clear from the definition at the beginning that reflection lets you create instances of a class at run time (while the program is running)! We can create an object using the class's full name. The class's full name is the class name, including the path of its package.
Reflection API: Reflection. The dark side of Java - 2
In my package hierarchy, the full name of MyClass would be "reflection.MyClass". There's also a simple way to learn a class's name (return the class's name as a String):

MyClass.class.getName()
Let's use Java reflection to create an instance of the class:

public static void main(String[] args) {
   MyClass myClass = null;
   try {
       Class clazz = Class.forName(MyClass.class.getName());
       myClass = (MyClass) clazz.newInstance();
   } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
       e.printStackTrace();
   }
   System.out.println(myClass); // Output: created object reflection.MyClass@60e53b93
}
When a Java application starts, not all classes are loaded into the JVM. If your code doesn't refer to the MyClass class, then ClassLoader, which is responsible for loading classes into the JVM, will never load the class. That means you have to force ClassLoader to load it and get a class description in the form of a Class variable. This is why we have the forName(String) method, where String is the name of the class whose description we need. After getting the Сlass object, calling the method newInstance() will return an Object object created using that description. All that's left is to supply this object to our MyClass class. Cool! That was difficult, but understandable, I hope. Now we can create an instance of a class in literally one line! Unfortunately, the described approach will only work with the default constructor (without parameters). How do you call methods and constructors with parameters? It's time to uncomment our constructor. As expected, newInstance() can't find the default constructor, and no longer works. Let's rewrite the class instantiation:

public static void main(String[] args) {
   MyClass myClass = null;
   try {
       Class clazz = Class.forName(MyClass.class.getName());
       Class[] params = {int.class, String.class};
       myClass = (MyClass) clazz.getConstructor(params).newInstance(1, "default2");
   } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
       e.printStackTrace();
   }
   System.out.println(myClass);// Output: created object reflection.MyClass@60e53b93
}
The getConstructors() method should be called on the class definition to obtain class constructors, and then getParameterTypes() should be called to get a constructor's parameters:

Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
   Class[] paramTypes = constructor.getParameterTypes();
   for (Class paramType : paramTypes) {
       System.out.print(paramType.getName() + " ");
   }
   System.out.println();
}
That gets us all of the constructors and their parameters. In my example, I refer to a specific constructor with specific, previously known parameters. And to call this constructor, we use the newInstance method, to which we pass the values of these parameters. It will be the same when using invoke to call methods. This begs the question: when does calling constructors through reflection come in handy? As already mentioned at the beginning, modern Java technologies can't get by without the Java Reflection API. For example, Dependency Injection (DI), which combines annotations with reflection of methods and constructors to form the popular Darer library for Android development. After reading this article, you can confidently consider yourself educated in the ways of the Java Reflection API. They don't call reflection the dark side of Java for nothing. It completely breaks the OOP paradigm. In Java, encapsulation hides and restricts others' access to certain program components. When we use the private modifier, we intend for that field to only be accessed from within the class where it exists. And we build the program's subsequent architecture based on this principle. In this article, we've seen how you can use reflection to force your way anywhere. The creational design pattern Singleton is a good example of this as an architectural solution. The basic idea is that a class implementing this pattern will only have one instance during execution of the entire program. This is accomplished by adding the private access modifier to the default constructor. And it would be very bad if a programmer used reflection ot create more instances of such classes. By the way, I recently heard a coworker ask a very interesting question: can a class that implements the Singleton pattern be inherited? Could it be that, in this case, even reflection would be powerless? Leave your feedback about the article and your answer in the comments below, and ask your own questions there!

More reading:

Comments (7)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
catalin1989 Level 37, Romania
4 December 2023
If I understood correctly the article, with reflection, you can bypass the private access modifier. This is a violation of the encapsulation principle :).
TaoLu Level 20, 泾县, China
13 April 2021
So, can a class that implements the Singleton pattern be inherited?
Andrei Level 41
26 February 2021
Big league info right here!
kiwii Level 37, United States
16 October 2019
This gets complicated.
Jen P Level 26
11 August 2019
I am confused about this: "If your code doesn't refer to the MyClass class, then ClassLoader, which is responsible for loading classes into the JVM, will never load the class." What does it mean "code does not refer to the MyClass class" ? Why it emphasizes "MyClass" , is there any special thing in class MyClass ?
Azja Level 32, Krakow, Poland
7 May 2019
What about security? Is reflection mechanism is way for hackers to get personal data from applications?