Previous in the Series
Current Tutorial
Reading and Writing Fields
Next in the Series

Previous in the Series: Reading Modifiers

Next in the Series: Invoking Methods

Reading and Writing Fields

A Field object let you get more information on the corresponding field: its type and its modifiers, and enables you to get the value of this field for a given object, and to set it. This section also covers how you can discover fields in a class.

 

Locating Field

There are two categories of methods provided in Class for accessing fields.

  1. First, you can look for a specific field. These methods suppose you have a name for the field you are looking for.
  2. Second, you can look for all the fields that are declared in this class, or for the fields declared in this class, and all its super classes, up to the Object class. In the first case, you will get all the fields: public, protected, default (package) access, and private. In the second case, you will get only the public fields.

The following tables provide a summary of all the member-locating methods and their characteristics.

Class API Array of fields? Inherited fields? Private fields?
getDeclaredField(String) no no yes
getField(String) no yes no
getDeclaredFields() yes no yes
getFields() yes yes no

Let us see these methods in action on the ArrayList class from the JDK.

First, let us print all the declared fields of this class.

Class<?> arrayListClass = Class.forName("java.util.ArrayList");
Field[] fields = arrayListClass.getDeclaredFields();
for (Field field : fields) {
    boolean isPublic = field.accessFlags().contains(AccessFlag.PUBLIC);
    System.out.println("Field: " + field.getName() +
                       " declared in " + field.getDeclaringClass().getSimpleName() +
                       " " + field.accessFlags());
}

Running this code prints the following.

Field: serialVersionUID declared in ArrayList [PRIVATE, STATIC, FINAL]
Field: DEFAULT_CAPACITY declared in ArrayList [PRIVATE, STATIC, FINAL]
Field: EMPTY_ELEMENTDATA declared in ArrayList [PRIVATE, STATIC, FINAL]
Field: DEFAULTCAPACITY_EMPTY_ELEMENTDATA declared in ArrayList [PRIVATE, STATIC, FINAL]
Field: elementData declared in ArrayList [TRANSIENT]
Field: size declared in ArrayList [PRIVATE]

As you can see, only the fields from the ArrayList class itself have been found, whatever their modifier is.

You can try the same code with the Field.getFields() method.

Class<?> arrayListClass = Class.forName("java.util.ArrayList");
Field[] fields = arrayListClass.getFields();
System.out.println("Public fields for ArrayList: " + fields.length);

Running this code prints the following.

Public fields for ArrayList: 0

Indeed, there is no public fields in ArrayList, nor in any of its super classes.

 

Obtaining Field Types

There are two ways to get information on the type of a field. The first and easiest one is to call its getType() method, that returns a Class object. This works well as long as the type of the field is not generic. If it is, then the type is just Object, which may give you ambiguous information.

Obtaining Non-Generic Field Types

The first way to get the type of a field is to use the getType() method, that returns a Class object.

Suppose that you have the following class.

class User {
    private String name;
}

Then you can get the type of the name field with the following code.

Class<?> userClass = User.class;
Field nameField = userClass.getDeclaredField("name");
Class<?> type = nameField.getType();
System.out.println("type = " + type.getName());

Running this code prints the following.

type = java.lang.String

Obtaining Generic Field Types

The previous method of getting the type of a field works as long as this type is not a generic type.

Suppose that you now have the following generic class.

class Box<T> {
    private T t;
}

And you look for the type of the field using the classical way.

Class<?> boxClass = Box.class;
Field field = boxClass.getDeclaredField("t");
Class<?> type = field.getType();
System.out.println("type = " + type.getName());

What you get is the erased type, which is Object.

type = java.lang.Object

This is why, starting with Java SE 5, the Field.getGenericType() method has been added to the Field class, that returns a Type object. This object has a single method getTypeName() that gives you the name of the generic type of this field.

Let us replace the previous code by the following.

Class<?> boxClass = Box.class;
Field field = boxClass.getDeclaredField("t");
Type genericType = field.getGenericType();
System.out.println("type = " + genericType.getTypeName());

Running the previous code now prints the following.

type = T

The exact type of the t field is now preserved, as it is declared in the class.

Note that this method cannot give you access to the real type at runtime, it can only give you the type you declared in the source code. You can see that on the following example. Suppose that you have the following Box class.

public static class Box<T> {
    private final T t;

    public Box(T t) {
        this.t = t;
    }
}

And the following code.

Box<String> box = new Box<>("Hello");
Class<?> boxClass = box.getClass();
Field tField = boxClass.getDeclaredField("t");
Type type = tField.getGenericType();
System.out.println("type = " + type);

Running the previous example prints the following.

type = T

 

Retrieving Field Modifiers

The modifiers of a field work in the same way as the modifiers of a class. The method Field.getModifiers() returns an int in which each bit represent a modifier. For instance, if the bit 0 is set, it means that this field is public. If the bit 3 is set, then this field is static. You can also use the static methods of the Modifier class to decode this integer.

Suppose you have the following User class:

class User {
    private String name;
}

You can get the modifiers of the field name with the following code:

Class<?> userClass = User.class;
Field nameField = userClass.getDeclaredField("name");

int modifiers = nameField.getModifiers();
System.out.println("isStatic:  " + Modifier.isStatic(modifiers));
System.out.println("isPublic:  " + Modifier.isPublic(modifiers));
System.out.println("isPrivate: " + Modifier.isPrivate(modifiers));
System.out.println("isFinal:   " + Modifier.isFinal(modifiers));

This code prints the following.

isStatic:  false
isPublic:  false
isPrivate: true
isFinal:   false

Note that you can also use Field.accessFlags() in the same way as you did on classes. You can see this method in action on the following example.

Class<?> userClass = User.class;
Field nameField = userClass.getDeclaredField("name");
Set<AccessFlag> flags = nameField.accessFlags();
System.out.println("Flags = " + flags);

Running the previous code prints the following.

Flags = [PRIVATE]

Also, remember that there is not a one to one correspondence between modifiers and access flags. You can read more on access flags in this section

 

Getting and Setting Field Values

The Reflection API allows you to read and update the value of fields through the use of Field objects. This is done through the use of two methods: Field.get(Object) and Field.set(Object,Object).

In both cases, you need to pass the instance you need to read or write, because the Field object is obtained from the Class, which is unique for a given class, and thus shared for all the instances of this class. Whatever method you use to get a reference to a specific class, you always get the same instance of Class. So the instance of the Field class you get for a given field of a given class is also unique.

Let us use the following class for our example.

class User {
    String name;

    public User(String name) {
        this.name = name;
    }

    public String toString() {
        return "User[name='" + name + "]";
    }
}

You can get the value of the name class for a given instance of this User class with the following code.

User maria = new User("Maria");
Class<?> userClass = maria.getClass();
Field nameField = userClass.getDeclaredField("name");
String name = nameField.get(maria);
System.out.println("name of maria = " + name);

Running this code prints the following:

name of maria = Maria

You can also update the name field with the following code.

nameField.set(maria, "Mary");

Dealing With Non-Visible Fields

You can notice that the field name from the User class is not private. The example we show can be run as long as the code that is calling nameField.set(maria, "Mary") can see the field name, which is visible from the same package.

If you make this field private, or if you move the calling code to another package, making the field name not visible from the calling code, you will get the following exception.

Exception in thread "main" java.lang.IllegalAccessException: class org.devjava.Main cannot access 
a member of class org.devjava.User with modifiers "private"
    at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:420)
    at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:709)
    at java.base/java.lang.reflect.Field.checkAccess(Field.java:1140)
    at java.base/java.lang.reflect.Field.get(Field.java:425)
    at org.devjava.Main.main(Main.java:16)

This exception tells you that the visibility rules are still holding for the Reflection API.

You can still get access to a private field, by telling the API that you want to suppress the checks for access control. This can be done with the following code.

nameField.setAccessible(true);
nameField.set(maria, "Mary");

This codes makes the field nameField accessible, despite being private. A common mistake is to think that this call to setAccessible(true) makes a field non-private, which is not the case.

Note that there are restrictions on the members you can make accessible, if there are in other modules. You can check the page Reflective Access with Open Modules and Open Packages for more information.

Dealing With Final Fields

The reading of final fields works the same as the reading of any field.

As you saw in the previous section, making a field accessible suppresses the checks for access control. That includes visibility and final modifiers.

Suppose that you now have this class, where the name field has been declared private and final.

class User {
    private final String name;

    public User(String name) {
        this.name = name;
    }

    public String toString() {
        return "User[name='" + name + "]";
    }
}

Now consider the following code. You are getting a field object for the name field of the User class, and you use it to update the value of the name field. Note the call to nameField.setAccessible(true).

User maria = new User("Maria");
System.out.println("Maria = " + maria);

Class<?> userClass = maria.getClass();
Field nameField = userClass.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(maria, "Mary");
System.out.println("Maria = " + maria);

Running the previous code prints the following. Despite the name field being private and final, you could update it using the Reflection API.

Maria = User[name=Maria]
Maria = User[name=Mary]

Setting the accessible flag to true on a field object gives you read and write access to this field. There are some exceptions though: it does not give you write access if the field is a static field, and it does not give you write access on the instance fields of a record.

 

Dealing with Primitive Types

As you know, primitive types can be automatically boxed to their wrapper types. You need to keep in mind that the Field.get(Object) returns an Object type, and that Field.set(Object,Object) methods takes an Object type. So when you are reading a field from an object using reflection, you need to cast the object you get to its exact type. Is your field has a primitive type or a wrapper type, you can cast the result of a call to Field.get(Object) to the corresponding primitive type or wrapper type.

When the type of the field you are working with is a primitive type, then you can avoid the overhead of boxing and unboxing this primitive type to its corresponding wrapper type by using one of the methods Field.getInt(Object) or Field.setInt(Object,int). There are methods for all the primitive types on the Field class, for both getting and setting the value of the field.

Be careful though, using such a method on a field that is not of the right type will throw an exception. Consider the following example, where we have added an age field of type int.

public class User {
    private String name;
    private int age;
    
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

You can run the following code.

User maria = new User("Maria", 32);
Class<?> userClass = User.class;
Field ageField = userClass.getDeclaredField("age");

int age = ageField.getInt(maria);
System.out.println("age = " + age);

ageField.setInt(maria, 33);
age = ageField.getInt(maria);
System.out.println("age = " + age);

This codes prints the following:

age = 32
age = 33

But if you change the type of the age field from int to Integer, then running the same code will produce the following exception.

Exception in thread "main" java.lang.IllegalArgumentException: 
Attempt to get java.lang.Integer field "org.devjava.User.age" with illegal data type conversion to int
    at java.base/jdk.internal.reflect.FieldAccessorImpl.newGetIllegalArgumentException(FieldAccessorImpl.java:130)
    at java.base/jdk.internal.reflect.FieldAccessorImpl.newGetIntIllegalArgumentException(FieldAccessorImpl.java:193)
    at java.base/jdk.internal.reflect.MethodHandleObjectFieldAccessorImpl.getInt(MethodHandleObjectFieldAccessorImpl.java:84)
    at java.base/java.lang.reflect.Field.getInt(Field.java:612)
    at org.devjava.Main.main(Main.java:40)

Last update: July 19, 2024


Previous in the Series
Current Tutorial
Reading and Writing Fields
Next in the Series

Previous in the Series: Reading Modifiers

Next in the Series: Invoking Methods