Field Reflection in Java
Field Reflection
Field reflection is a runtime feature that allows you to dynamically read fields and write to fields in a class at runtime. You can also find what modifiers and type the field is marked with. Field reflection is done using the java.reflect.Field class. Instances of this class can be obtained from java.lang.Class. The following class will be used for the examples in this article.
package press.bytesize;
public class User {
private final int id = 10;
}
Obtaining java.reflect.Field Instances
Once a java.lang.Class instance is obtained for the class you are wanting to use reflection on, you can obtain a java.reflect.Field instance a few different ways depending on your use case. Unlike java.lang.Class, java.reflect.Field instances are not singletons.
The id java.reflect.Field in the User class can be obtained using the getDeclaredField() method.
final Field idField = User.class.getDeclaredField("id");
If the id field didn't exist, this would throw a NoSuchFieldException.
All fields in the User class can be obtained using the getDeclaredFields() method. You can then use the getName() method on each of the fields to get the field name.
final Field[] fields = User.class.getDeclaredFields();
for (final Field field : fields) {
if ("id".equals(field.getName())) {
System.out.println("Found field 'id'");
break;
}
}
This gets all the fields in the User class. It then loops through each until it finds the id field and then prints a message.
If you are working with public fields, you can use getField() and getFields() instead. These will only return a field if it is public and will thrown a NoSuchFieldException if the field doesn't exist or if it isn't public.
java.reflect.Field Class
Fields have modifiers, a type, a name, and a value assigned to that field. You can print out the code for a field in a class using the following:
final Field idField = User.class.getDeclaredField("id");
System.out.println(idField.toGenericString());
This will print the following:
private final int press.bytesize.User.id
Field Accessibility
In order to work with a value of a field on a class, you must have an instance of that class. Depending on if you are getting or setting a value and the access modifier on the field, you will have to make the field accessible before you can get or set a value. You do this through the setAccessible() method. If a field cannot be accessed due to the lack of permission defined by the SecurityManager, a SecurityException will be thrown. These permissions are different from the access modifier.
final Field idField = User.class.getDeclaredField("id");
idField.setAccessible(true);
Java 9 introduced the trySetAccessible() method. This will return true if you have permissions to make it accessible; otherwise, it will return false.
final Field idField = User.class.getDeclaredField("id");
if (idField.trySetAccessible()) {
// Do something...
} else {
// You do not have permissions...
}
Getting a Field Value
All field values, unless they are marked as private, are always accessible, whether you have permissions or not. With private fields, you will have to set them to accessible before you can obtain the value. You can get the value of a field using the get() method.
final var user = new User();
final Field idField = user.getClass().getDeclaredField("id");
idField.setAccessible(true);
final Object id = idField.get(user);
There are also other methods you can use to get a value strongly typed. There is a method for each primitive type. They are getBoolean(), getByte(), getShort(), getInt(), getLong(), getFloat(), getDouble(), and getChar(). Getting the value of the id field in the above example could be done with the following instead.
final int id = idField.getInt(user);
Setting a Field Value
Setting a value on a field regardless of the access modifier, the field always needs to be accessible first. Once it is accessible, you can set the value using the set() method.
final var user = new User();
final Field idField = user.getClass().getDeclaredField("id");
idField.setAccessible(true);
idField.set(user, 15);
This sets the id field to the value 15.
Just like when getting a value, you have strongly typed versions for primitive types as well. These methods are setBoolean(), setByte(), setShort(), setInt(), setLong(), setFloat(), setDouble(), and setChar(). Setting the id field in the above example could be done with the following instead.
final var user = new User();
final Field idField = user.getClass().getDeclaredField("id");
idField.setAccessible(true);
idField.setInt(user, 15);
Field Type
You can get the type of the field using the getType() method. This returns an instance of java.lang.Class.
final Field idField = User.class.getDeclaredField("id");
final Class<?> idType = idField.getType();
Field Modifiers
You can find out if a field is marked with a specific modifier using the getModifiers() method. This returns an int. You can then use the java.lang.Modifier class to see if a field has a specific modifier.
final Field id = User.class.getDeclaredField("id");
System.out.println(Modifier.isPrivate(id.getModifiers())); // true
System.out.println(Modifier.isFinal(id.getModifiers())); // true
System.out.println(Modifier.isTransient(id.getModifiers())); // false
Java 20 introduced the accessFlags() method. This returns a java.util.Set of java.lang.reflect.AccessFlags. You can then check to see if the Set contains an AccessFlag.
final Field id = User.class.getDeclaredField("id");
final Set<AccessFlag> flags = id.accessFlags();
System.out.println(flags.contains(AccessFlag.PRIVATE)); // true
System.out.println(flags.contains(AccessFlag.FINAL)); // true
System.out.println(flags.contains(AccessFlag.TRANSIENT)); // false
Superclass Fields
When working with field reflection, you will only be able to access the fields in the Java source file you are working in. Fields are not inherited with reflection. It is only what is defined in the Java source file that you can access for that class. If you have a field in a superclass you are wanting to access, you have to specify the superclass instead to get to the field. If the User class had a superclass, you could access it using the getSuperclass() method.
public class SuperclassUser {
private String field;
}
public class User extends SuperclassUser {
}
final Field superField = SuperclassUser.class.getDeclaredField("field");
// or
final Field subField = User.class.getSuperClass().getDeclaredField("field");
Conclusion
Field reflection allows you to query for modifiers, the type and value. Using reflection, as long as it is permitted through the SecurityManager, you can get and set the value of fields even if they are final and private.