In this series I want to take a deep dive into the realm of the Java reflection library. This library contains features that can give you immense power when used correctly. However, as we all know, with great power comes great responsibility.
This is my fair warning that I am not an expert on the reflection library. I am learning it myself. I just want to document my progress and hopefully help someone else along the way. Anyway, let’s get started.
The reflection library is essentially a classpath scanning tool. It allows you to get information about the other files in a java project. This is super helpful when you want to write highly scalable code that is easy to maintain.
Class Information
- A Java class contains information including the classes fields and methods, its runtime type, and classes it extends, and any interfaces implemented by it. There are three ways you can get a class object from some instance of a class.
-
-
// 1 Object.getClass(); // 2 String.class; // 3 Class.forName("SomeClassName");
- The
Object.getClass()
works for custom classes as well as non-primitive built-in classes. - The
Primitive.class
method works for accessing class information about primitive classes. - The
Class.forName("Name");
does not work for primitives but allows you to access nested / sub-classes. You can do this by writingClass.forName("MainClass$SubClass");
adding the subclass after a dollar sign
-
-
Class Constructors
- Accessing the constructors of a class allows the creation of a class creation method that can create objects of any class in the project. It also allows a developer to see what options they have in order to instantiate a class properly. There are two main ways to go about this.
-
-
// 1 Class.getConstructors(); // 2 Class.getDeclaredConstructors();
- The
Class.getConstructors()
method will return only the publicly avaliable constructors. - The
Class.getDeclaredConstructors()
method returns all constructors in the class regardless of their visibility. (public and non-public)
-
- If a class has no declared constructors and you call either of the above methods on it, you will get the default constructor back. This will be in the form of a single element array.
- Once you have gotten a list of constructors for a given class, you can loop over them the determine what parameters they require. To do this you can call
constructorObject[i].getParameterTypes();
-
Class Fields
- Accessing the fields of a class is very easy. It can also be incredibly useful. Similar to the methods used to access a classes constructors, there are two main options. One to get the public fields and one to get all fields, public and non-public.
- If you want to return all fields of a class you will use one of the two following methods. They both return an array
-
// 1 Class.getFields(); // 2 Class.getDeclaredFields();
- The
Class.getFields()
method will return only the publicly avaliable fields. - The
Class.getDeclaredFields()
method returns all fields in the class regardless of their visibility. (public and non-public)
-
- You can also search for a specific field.
-
// 1 Class.getField(name); // 2 Class.getDeclaredField(name);
-
- If you want to return all fields of a class you will use one of the two following methods. They both return an array
Example
- In this example I’m using a class called Coin which represents a type of currency or cryptocurrency.
- This is the Coin class.
-
public class Coin { private String price; private boolean confirmedSupply; private int numberOfMarkets; public Coin(String _price) { this.price = _price; } public Coin(String _price, int _numberOfMarkets) { this.price = _price; this.numberOfMarkets = _numberOfMarkets; } public Coin(String _price, int _numberOfMarkets, boolean _confirmedSupply) { this.price = _price; this.numberOfMarkets = _numberOfMarkets; this.confirmedSupply = _confirmedSupply; } private static class CoinType { private boolean isCrypto; protected void setCrypto(boolean _isCrypto){ this.isCrypto = _isCrypto; } } }
- This is the main method and two additional methods which loop over properties of the Coin class and prints corresponding data.
-
import models.Coin; import java.lang.reflect.Constructor; import java.lang.reflect.Field; public class Main { public static void main(String[] args) throws IllegalAccessException { Coin coin = new Coin("153"); System.out.println("====CONSTRUCTORS===="); getConstructors(Coin.class); System.out.println("====FIELDS===="); getDeclaredFields(Coin.class, coin); } public static <T> void getConstructors(Class<? extends T> klass) { for (Constructor constructor : klass.getDeclaredConstructors()) { System.out.println("Constructor Name: " + constructor.getName()); System.out.println("Declared By: " + constructor.getDeclaringClass()); System.out.println(); } } public static <T> void getDeclaredFields(Class<? extends T> klass, T instance) throws IllegalAccessException { for (Field field : klass.getDeclaredFields()) { System.out.println("Field Name: " + field.getName()); System.out.println("Type: " + field.getType().getName()); System.out.println(); } } }
- This is the console output from running the above code.
-
====CONSTRUCTORS==== Constructor Name: models.Coin Declared By: class models.Coin Constructor Name: models.Coin Declared By: class models.Coin Constructor Name: models.Coin Declared By: class models.Coin ====FIELDS==== Field Name: price Type: java.lang.String Field Name: confirmedSupply Type: boolean Field Name: numberOfMarkets Type: int
Thanks for reading! I plan on posting additional reflection library content soon!
-Kyle