September 16, 2015 · java reflection meta metaprogramming ·

Intro to Reflection in Java

Recently, I began exploring an interesting idea I had - creating a RESTful framework built ontop of Spark. Now, Spark is naturally restful, but I basically want the user to be able to define an object, flag it as a resource, and compile their code. They should then have GET, POST, PUT, and DELETE verbs available on that object. Sounds relatively trivial right?

There are actually several ways to do this, but the simplest way (once I was able to wrap my head around it) involves metaprogramming, and specifically reflection. These can be naturally scary words, but it's important to keep in mind, they're both just programming.

That being said, there seems to be infinitely more resources for ANY other Java API on the web than there are for reflection. So, hopefully this post will be helpful, as I'm going to describe some of the topics and actions that I wished I didn't have to dig into the Java APIs to figure out.

If, at any point, you're interested in seeing this framework project that I'm working on, famously named Ginger, feel free to click here. Don't worry, this tutorial isn't going anywhere.

What is reflection?

When you look in a mirror, you see a reflection. You're able to inspect qualities about yourself that wouldn't normally see, such as, the birthmark on your ear, or the color of your eyes. Reflection, in Java, is quite similar. A class can inspect itself, or another class, and provide data about that class. Since data is just data, we're actually able to act on that data to make decisions and execute code.

One of the things to keep in mind, Java is naturally a very verbose language, and that's because it likes to know exactly what you're doing and let you know if something is going to go wrong when compiling. Reflection is often extremely abstract, and you may have to stray away from the safety net the compiler sets up for you - especially if other people are writing the code that your code reflects upon.

Reflecting upon a class

When setting up reflection for the first time, one of the first things you'll want to do is define the class that you're going to reflect upon. If, for example, we have a MyLinkedList class that we want to reflect upon, we could write something like the following

    Class reference = MyLinkedList.class;

It's that simple! Let's go ahead and actually define that class in a separate file.

    public class MyLinkedList {
      private Object head;
      private int count;
      public String name;

      public MyLinkedList(){
        this.head = new Object();
        this.count = 0;
        this.name = "LinkedListHero";
      }

      public void add(Object o){
        //do something 
        System.out.println("Called 'add' with object: "+o);   
      }

      public void get(int index){
        //do something else
        System.out.println("Called 'get' with index: "+index);
      }

      public static void sayHello(){
        System.out.println("Hello");
      }
    }

We've created this pseudo-LinkedList and I've just stubbed out the add and get methods, as their actual implement is mostly irrelevant. Additionally, there's a a name property, and a sayHello() method that aren't really relevant to the LinkedList, but we're going to use them to demo some reflection. So we've defined out class, and in a separate file, we created a reflective reference to that class. Now we can get to the cool stuff!

Getting Member Variables

Let's do something useful. We're going to get all the member variables that are available to the class at runtime.

    for(Field f: reference.getFields())
        System.out.println(f.getName());

Will print out...

  name

Notice that it's not printing head or count. Indeed, this returns only the public fields that we've declared. Additionally, if our LinkedList class inherited from another class with public members, they would display here, too!

But what if I need private members too?

If you're in need of private member variables, then you're in luck!
instead of using .getFields(), you can use getDeclaredFields(). This will return all of the fields that have been declared on that class and that class only. That is, if we inherited public members from another class, getDeclaredFields() would not show them here.

More with fields

I'll quickly cover a few common methods of the field class.

getName()
    for(Field f: MyLinkedList.class.getFields())
      f.getName()

Simply returns the name of the field.

getType()
    for(Field f: MyLinkedList.class.getFields())
      f.getType()

Returns String, int, double, Object, etc.

Methodical Madness with Methods

Personally, I believe the power of reflection really shines with the use of methods. They function quite similar to Fields, actually, but can do quite a bit more than just reflective fields.

Let's get right down to it. If you want to get the member variables of the class (remember, these are the variables set at runtime), you can do so with the following:

    Class reference = MyLinkedList.class;
    Method[] methods = reference.getMethods();

    for(Method m: methods)
      System.out.println(m.getName());

This will not only get all the methods that are available at runtime, but will print the name of them too.

Your console should give you something like this...

  add
  get
  sayHello
  wait
  wait
  wait
  equals
  toString
  hashCode
  getClass
  notify
  notifyAll

Notice that it's also printing out the methods that our class inherited, such as toString and hashCode. This is because reflection doesn't inspect what code has been written, it inspects the code that is being ran at runtime.

Getting a single method by name

Additionally, provided you know the name of the method, you can retrieve that single method with a string. Careful: this call can throw a NoSuchMethodException.

    Method add = MyLinkedList.class.getMethod("add", new Class[]{Object.class});

This roughly translate to find the add method that takes in a parameter of an Object., which, if you remember, is the add(Object o) method we defined on our LinkedList above.

If the method takes no parameters, such as our sayHello(), you can pass null in as the second parameter.

Checking Method Parameters

That's pretty cool, but just knowing the name of the method isn't all that practical. Let's inspect the parameters that can be passed into the add() method.

    Method[] methods = MyLinkedList.class.getMethods();
    for(Method m: method)
      m.getParameterTypes();

Checked Method Return Values

Additionally, we can check the type of value being returned from our methods like so.

    Method[] methods = MyLinkedList.class.getMethods();
    for(Method m: method)
      m.getReturnType();

Invoking Methods

Alright, the moment you've all been waiting for - Invoking methods. Once you have a reference to your method, it's quite simple.

    Method add = MyLinkedList.class.getMethod("add", new Class[]{Object.class});
    add.invoke(new MyLinkedList(), "parameter1");

You can also invoke the static method, sayHello() like so.

    Method hello = MyLinkedList.class.getMethod("sayHello", null);
    hello.invoke(null);

Pretty cool right? Well, that's all I have for you for right now. Remember, metaprogramming is just programming from a different perspective! Also, feel free to comment below on what you'd like to see next from me!

Thanks for reading!

  • LinkedIn
  • Tumblr
  • Reddit
  • Google+
  • Pinterest
  • Pocket
Comments powered by Disqus