We saw in the previous article how to get information about constructors of a class. Now we’re going to talk about the Field class. In Java, a field is a variable declared inside a class or interface; these fields can be static (class variables), or instance variables, which means their values are different for each object. The Reflection Field class represents each one of those fields, and it contains properties such as field name and field value. We can use the Field class to get information about a class’s or object’s fields dynamically at runtime.
How to get the Field objects of a class
There are some ways we can get the Field objects of a class or interface:
– Class.getDeclaredFields(): gets all declared fields of the class, regardless of their access modifiers, but excludes inherited fields.
– Class.getFields(): gets all the public fields, including inherited fields.
– Class.getDeclaredField(fieldName): gets the Field object corresponding to the declared field with the given name. If fieldName doesn’t exist, a NoSuchFieldException is going to be thrown.
– Class.getField(fieldName): gets the public field with the given name, including inherited fields. Again, a NoSuchFieldException is thrown if fieldName doesn’t exist.
Let’s see an example:
package org.example.reflection;
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) {
printDeclaredFields(Movie.class);
}
public static void printDeclaredFields(Class<?> clazz) {
for (Field field : clazz.getDeclaredFields()) {
System.out.println(String.format("Field name: %s; Field type: %s",
field.getName(), field.getType()));
System.out.println();
}
}
public static class Movie extends Product {
public static final double MINIMUM_PRICE = 10.0;
private boolean isRelease;
private Category category;
private double price;
public Movie(String name, int year, boolean isRelease, Category category, double price) {
super(name, year);
this.isRelease = isRelease;
this.category = category;
this.price = price;
}
}
public static class Product {
protected String name;
protected int year;
protected double price;
public Product(String name, int year) {
this.name = name;
this.year = year;
}
}
public enum Category {
ADVENTURE,
ACTION,
DRAMA,
COMEDY
}
}
This is the output:
Field name: MINIMUM_PRICE; Field type: double
Field name: isRelease; Field type: boolean
Field name: category; Field type: class org.example.reflection.Main$Category
Field name: price; Field type: double
In the example above, we have a Main class, and inside of it we declared some inner classes: Product, Movie, and the enum Category; Movie is a subclass of Product. Then we declared the static method printDeclaredFields() that is going to print the field name and field type. Notice that the inherited fields were not printed in the output because we are using Class.getDeclaredFields() method.
Synthetic fields
Synthetic fields are artificial fields for internal usage generated by the Java Compiler. Such fields and their names are compiler specific, and we generally don’t want to rely or modify them. We can use the method Field.isSynthetic() to check if a field is synthetic.
Let’s see an example:
package org.example.reflection;
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) {
printDeclaredFields(Movie.MovieStats.class);
}
public static void printDeclaredFields(Class<?> clazz) {
for (Field field : clazz.getDeclaredFields()) {
System.out.println(String.format("Field name: %s; Field type: %s",
field.getName(), field.getType()));
System.out.println("Field is synthetic: " + field.isSynthetic());
System.out.println();
}
}
public static class Movie extends Product {
public static final double MINIMUM_PRICE = 10.0;
private boolean isRelease;
private Category category;
private double price;
public Movie(String name, int year, boolean isRelease, Category category, double price) {
super(name, year);
this.isRelease = isRelease;
this.category = category;
this.price = price;
}
public class MovieStats {
private double timesWatched;
public MovieStats(double timesWatched) {
this.timesWatched = timesWatched;
}
public double getRevenue() {
return timesWatched * price;
}
}
}
public static class Product {
protected String name;
protected int year;
protected double price;
public Product(String name, int year) {
this.name = name;
this.year = year;
}
}
public enum Category {
ADVENTURE,
ACTION,
DRAMA,
COMEDY
}
}
Field name: timesWatched; Field type: double
Field is synthetic: falseField name: this$0; Field type: class org.example.reflection.Main$Movie
Field is synthetic: true
This is similar to the first example, but here we’ve added an inner class inside Movie called MovieStats. Notice that, from the getRevenue() method, that the inner class has access to the outer class’s price variable. This indicates that, somehow, the MovieStats class has some kind of link to the instance of the Movie class. As we can see from the output, aside from the timesWatched field, we have another field, this$0 of type Movie, which is the synthetic field.
Another example of synthetic field is when we pass to the method printDeclaredFields() of the above program the enum Category as argument. In this case, we’re going to have this output:
Field name: ADVENTURE; Field type: class org.example.reflection.Main$Category
Field is synthetic: falseField name: ACTION; Field type: class org.example.reflection.Main$Category
Field is synthetic: falseField name: DRAMA; Field type: class org.example.reflection.Main$Category
Field is synthetic: falseField name: COMEDY; Field type: class org.example.reflection.Main$Category
Field is synthetic: falseField name: $VALUES; Field type: class [Lorg.example.reflection.Main$Category;
Field is synthetic: true
Notice the $VALUES field; from its type we can conclude that this is an array of Category. This is a field generated by the compiler that holds all possible values of the enum.
Getting Field values
We can also get the values of a Field of a specific object. Let’s see an example similar to our known program:
package org.example.reflection;
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) throws IllegalAccessException {
Movie movie = new Movie("The Shawshank Redemption", 1994, false, Category.DRAMA, 10.0);
printDeclaredFields(Movie.class, movie);
}
public static <T> void printDeclaredFields(Class<? extends T> clazz, T instance) throws IllegalAccessException {
for (Field field : clazz.getDeclaredFields()) {
System.out.println(String.format("Field name: %s; Field type: %s",
field.getName(), field.getType()));
System.out.println("Field is synthetic: " + field.isSynthetic());
System.out.println(String.format("Field value: %s", field.get(instance)));
System.out.println();
}
}
public static class Movie extends Product {
public static final double MINIMUM_PRICE = 10.0;
private boolean isRelease;
private Category category;
private double price;
public Movie(String name, int year, boolean isRelease, Category category, double price) {
super(name, year);
this.isRelease = isRelease;
this.category = category;
this.price = price;
}
public class MovieStats {
private double timesWatched;
public MovieStats(double timesWatched) {
this.timesWatched = timesWatched;
}
public double getRevenue() {
return timesWatched * price;
}
}
}
public static class Product {
protected String name;
protected int year;
protected double price;
public Product(String name, int year) {
this.name = name;
this.year = year;
}
}
public enum Category {
ADVENTURE,
ACTION,
DRAMA,
COMEDY
}
}
In this version of the program we changed the method printDeclaredFields() to make it generic and take another argument of a generic type called instance. Then we instantiate a Movie object and call printDeclaredFields() passing the type Movie and the object we just instantiated. This is the output:
Field name: MINIMUM_PRICE; Field type: double
Field is synthetic: false
Field value: 10.0Field name: isRelease; Field type: boolean
Field is synthetic: false
Field value: trueField name: category; Field type: class org.example.reflection.Main4$Category
Field is synthetic: false
Field value: DRAMAField name: price; Field type: double
Field is synthetic: false
Field value: 10.0
Notice that, for the static field MINIMUM_PRICE we don’t actually need an object, because it is a class field, which means the value is stored in the class itself. To prove that statement, we could get the value of the specific field like so:
...
Field minimumPrice = Movie.class.getDeclaredField("MINIMUM_PRICE");
System.out.println(String.format("Static MINIMUM_PRICE value: %s", minimumPrice.get(null)));
...
Static MINIMUM_PRICE value: 10.0