What is reflection?

If you are a Java developer you probably already heard this jargon, but do you really know what it is?

Definition

If you look for a definition online, you may find something like:

Reflection is a feature in the Java programming language. It allows an executing Java program to examine or “introspect” upon itself, and manipulate internal properties of the program.

https://www.oracle.com/technical-resources/articles/java/javareflection.html

The concept may sound a bit opaque, but it is actually quite simple. So let’s dig a bit on that.

First off, it is important to note that the concept of reflection is not particular to Java. Many programming languages support it. The way they tackle it may differ slightly, but conceptually it is all the same.

Dissecting

When you define a class in Java, for instance, you are able to produce code that directly refers any of its members (properties or methods), as long as you have the proper visibility for them (defined by access modifiers). These references you do in code may be validated by an IDE, which will immediately complain if they are wrong. But even if it doesn’t, ultimately they will fail during compilation.

But what if you wanted to make such references and you didn’t know exactly what to refer to at compilation time? If those references are dynamic, you may have to assess them in runtime. This is where reflection is valuable.

Did you ever noticed that Java has this special class called Class? Ever wondered what’s its purpose? You can think about it as a special class that every object will hold a reference to an instance of. That instance will hold metadata about that class, which allows you to perform reflection over it.

Confused?

Lets dig some examples. Imagine you have the following class:

@Example
public class Rho {
    public String publicForShowcase ="Rho Systems Lda";
    private boolean hiring = true;
    private String email = "hr@rho.pt";

    public Rho(boolean hiring, String email) {
        this.hiring = hiring;
        this.email = email;
    }

    public boolean isHiring() {
        return hiring;
    }

    public String getEmail() {
        return email;
    }

    public String generateEmail(String firstName, String lastName) {
        return "%s.%s@rho.pt"
               .formatted(firstName.toLowerCase(),
                          lastName.toLowerCase());
}

Now imagine you had a class and you wanted to introspect it. You could do that by doing:

//Static way to get metadata
Class clazz = Rho.class;

//Dynamic way to get metadata, given an instance
Rho rho = new Rho();
clazz = rho.getClass(); 

//ex: Get metadata for all fields
Field[] fields = clazz.getFields(); 

//ex: Get metadata for a specific field
Field field = clazz.getField("hiring");

//ex: Get metadata for all methods
Method[] methods = clazz.getMethods();

//ex: Get metadata for a specific method
Method[] methods = clazz.getMethod("isHiring");

//ex: Get metadata for all annotations
Annotation[] annotations = clazz.getAnnotations();

//ex: Get metadata for a specific annotation
Annotation annotation = clazz.getAnnotation(Example.class);

//etc... go and explore!!!

Pretty cool uh? Now what if you wanted to dynamically get the value of a property, for instance? You can just leverage on the metadata and ask for it to introspect your specific instance:

Rho rho = new Rho();

//Get value of field "publicForShowcase"
Field field = rho.getClass().getField("publicForShowcase");
String value = (String) field.get(rho);
//This will return "Rho Systems Lda"

Did you notice we had to cast the return of the get? That is because the type can only be determine at runtime, so it returns an Object. You will have to downcast to the specific class you know is being returned.

Note that even if you are using reflection, you are not allowed to access a field which access modifier indicates you are not supposed to:

Rho rho = new Rho();

//Get value of field "email"
Field field = rho.getClass().getField("email");
String value = field.get(rho);
//This will raise a runtime error, as the field is private

You can, however, use getters/setters in order to handle a property, just like you would without reflection:

Rho rho = new Rho();

//Get value of field "email"
Method method = rho.getClass().getMethod("getEmail");
String value = (String) method.invoke(rho);
//This will return "hr@rho.pt"

Again, notice the need for downcasting!

Imagine now a case on which you wanted to call a method with arguments:

Rho rho = new Rho();

Method method = rho.getClass().getMethod("generateEmail");
String email = (String) method.invoke(rho, "Nuno", "Marujo");
//This will return "nuno.marujo@rho.pt"

There are plenty more things you can do with reflection, but at this point I think you got the concept and you can extrapole the rest!

Now that you know a bit more about reflection, go and explore it. Let me know about any doubts you may have on the comment section.

Discover more from Engineering @ Rho

Subscribe now to keep reading and get access to the full archive.

Continue reading