Reflection API in Java
Where is this used?
This is used to analyze/modify the behaviour of a class at runtime. Using this, you can view or change the private/public fields at wish (without exposing any getter/setter). Personally, I have used this in one of our projects at GreyOrange to write unit test cases. Using this in main code is a big no-no as it exposed you critical fields to the world.
Main Class
Let’s create a main class for which we will write some test cases. But we want to test some private fields for which we don’t have a direct getter. The idea is to use reflection api to access such fields and fetch their current value or modify them if required.
Here is a Duck class which has 3 fields of which 1 is static. Each time a duck class is created count which is the static field is increased by one. Each duck has an associated name and age.
public class Duck {
private String name;
private int age;
private static int count = 0;
public Duck(String name, int age) {
this.name = name;
this.age = age;
count++;
}
public static boolean canCreateMoreDucks() {
return count < 10;
}
public String getName() {
return name;
}
public boolean canDrinkAlcohol() {
return age >= 18;
}
Test Class – uses reflection API
Change the value of a private field inside a class
Field
and getDeclaredField
are used to access a variable.
Using setAccessible
as true will expose any private fields which can be manipulated.
@Test
public void testDuckCanDrinkAlcohol() {
Duck duck = new Duck("Donald", 5);
assertEquals("Donald", duck.getName());
assertFalse(duck.canDrinkAlcohol());
// change age and check if duck can drink alcohol
// But I don't want to create a setter for this
// Use reflection API to change the age
try {
Class<Duck> duckClass = Duck.class;
Field ageField = duckClass.getDeclaredField("age");
ageField.setAccessible(true);
ageField.setInt(duck, 20);
}
catch (Exception e) {
e.printStackTrace();
assert false;
}
assertTrue(duck.canDrinkAlcohol());
}
Get the value of a static private variable in a class
A static
field can be accessed in the similar way.
@Test
public void testDuckCanCreateMoreDucks() {
// Instead of creating more ducks
// I will use reflection API to change the count
Duck duck = new Duck("Donald", 5);
assertTrue(Duck.canCreateMoreDucks());
// Also assert count was 1
// But I don't want to create a getter for this
try {
Class<Duck> duckClass = Duck.class;
Field countField = duckClass.getDeclaredField("count");
countField.setAccessible(true);
// Don't need to pass an instance as count is static
Object countObject = countField.get(null);
int count = (int) countObject;
assertEquals(1, count);
}
catch (Exception e) {
e.printStackTrace();
assert false;
}
}
Change the value of a static private variable in a class
You can use setInt
to change the value of the Field
.
@Test
public void testDuckCannotCreateMoreDucks() {
// Instead of creating more ducks
// I will use reflection API to change the count
Duck duck = new Duck("Donald", 5);
// change count to 10
try {
Class<Duck> duckClass = Duck.class;
Field countField = duckClass.getDeclaredField("count");
countField.setAccessible(true);
countField.setInt(null, 10);
}
catch (Exception e) {
e.printStackTrace();
assert false;
}
assertFalse(Duck.canCreateMoreDucks());
}