Intro to Java Reflection

This entry is part 6 of 13 in the series Intro to Java

Reflection is a very interesting part of Java. It is sometimes considered an advanced topic, but I think it is worth exploring here. There are several practical applications for using reflection, and we will look at a few here.

Sun Trails

Index: http://java.sun.com/docs/books/tutorial/index.html
Link into the Reflections Trail: http://java.sun.com/docs/books/tutorial/reflect/index.html

What is reflection?

Reflection is a little like cheating. It allows you to get access to classes and its methods and variables without accessing them the normal way. We don’t use the new operator to create an instance, and by using reflection we can get access even to private variables and methods. For this reason, we should be careful about using reflection. It is a powerful way of writing programs, but be sure not to overuse it.

How do we get a class?

Remember when we talked about the difference between Classes and Objects, the class was the structure, definition, or blue prints. The object was when we created an instance of those plans. We can get any classes definition (such as Bird) by asking for its class property.

Class c = Bird.class;

This Class object is a special object that can tell us the structure of an object. It is this Class object that we will use to create an instance of the Bird class.

Can we get an object from this Class?

Absolutely. What would we normally do to get an instance object? New.

Bird aBird = new Bird();

What we will do now instead is use the Class (c) that we got above. We ask it to give us an instance. Because Class is generic and needs to work with many objects, we need to cast the object we get back into a Bird.

Bird aBird = (Bird) c.newInstance();

There. This Bird, that we called aBird is exactly the same as if we had used the new operator. There is no difference. It is a little cumbersome, but we could use that method all the time if we wanted.

ReflectiveTest1 shows an example with all of the lines together.

/**
 * Very basic example of reflection. We are creating a Bird object.
 * 
 * @author Chris Ward 
 */
public class ReflectiveTest1 {

   @SuppressWarnings("unchecked")
   public static void main(String[] args) throws InstantiationException, IllegalAccessException {

      // Every Class, like Bird, or Fish has a Class that represents it's
      // structure. You can access this via the class property.
      Class c = Bird.class;
		
      //Calling c.newInstance() is the same as saying new Bird()
      //Bird aBird = new Bird();
      //throws InstantiationException and IllegalAccessException
      Bird aBird = (Bird) c.newInstance();
		
      //this is just a Bird object like any other we have seen.
      aBird.setName("Tweety");
		
      System.out.print(aBird.getName());
   }
}

How useful is this if we start with a class we know?

True, it doesn’t seem to exciting if we start with Bird, ask for its class property, and then create an instance. It would be much easier to just use the new operator. But, Java lets us access a static method on Class to get any class based on its name.

First we need a name. We need the full path to a class’s name. The class property allows us to get this name as well. Let’s use that so that we know for sure we have the class’s correct name. Once we have the name, we can get a Class, and then we get an instance just like before.

String aBirdClassName = Bird.class.getName();
Class aBirdClass = Class.forName(aBirdClassName);
Bird aBird = (Bird) c.newInstance();

So, we have learned that you can ask any class for its Class property, and that it holds the full name of the class in String form. We have also seen that we can then take that class name and get a Class back. Then as we learned earlier, we can create an instance.

This is all put together in ReflectiveTest2.

/**
 * Example to show that we can use reflection to create an instance of a class.
 * We need to first get an Object called Class, then we can get an instance of
 * that Class.
 * 
 * @author Chris Ward 
 */
public class ReflectiveTest2 {

   @SuppressWarnings("unchecked")
   public static void main(String[] args) throws Exception {

      // If every class has a "class" property, we can use it to get the full
      // class name. Doing this is a little silly, but just shows a way to get
      // a string for the class name that we can use.
      String aBirdClassName = Bird.class.getName();

      // We first get a class from the class name. This is not an instance of
      // the class. This is only the structure and definition. Note, that what
      // gets returned is a Class. This is a special object in Java that
      // represents a class. The instance that gets returned represents this
      // particular Class.
      Class aBirdClass = Class.forName(aBirdClassName);

      // Now we take the class and give it life. This is as if we did a new to
      // the class with a zero argument constructor.
      // 
      // newInstance always returns an Object, so it needs to be cast to
      // something
      Object aBirdObject = aBirdClass.newInstance();
      Bird aBird = (Bird) aBirdObject;

      // We would usually just do this:
      // Animal aAnimal = (Animal) c.newInstance();

      // For good measure, we are setting the name here.
      aBird.setName("Tweety");

      System.out.println(aBird.getName());
   }
}

This is getting interesting, but we did start with the Bird class to get its name.

Using Reflection without knowing the name of the class.

ReflectiveTest3 gets a little more complicated. Let’s say that we know that if we are going to be creating Fish, Bird, and Mammal Objects. These are all Animals, aren’t they? ReflectiveTest3 creates a method that will take any class name, and if it is the full class name to an Object that is an instance of Animal, it will return an Animal for you. (Otherwise you will get errors)

This isn’t any special reflective magic. It is just how we are casting. Remember the reflection API is just dealing with generic Class and Object objects. We need to tell it what they are by casting. So, look at the createAnimal method:

/**
 * Now we start to make this more generic by creating a static method that will
 * do the reflection part for us. Now we pass in a string, and the method
 * returns an Animal. What happens if the string isn't a real class, or isn't an
 * animal?
 * 
 * @author Chris Ward 
 */
public class ReflectiveTest3 {

   /**
    * Method to create an Animal from the fully qualified name of the class.
    * 
    * @param pAnimalType
    * @param pName
    * @return
    * @throws ClassNotFoundException
    * @throws InstantiationException
    * @throws IllegalAccessException
    */
   @SuppressWarnings("unchecked")
   public static Animal createAnimal(String pAnimalType, String pName)
         throws ClassNotFoundException, InstantiationException,
         IllegalAccessException {

      // We first get a class from the class name. This is not an instance of
      // the class. This is only the structure and definition. Note, that what
      // gets returned is a Class. This is a special object in Java that
      // represents a class. The instance that gets returned represents this
      // particular Class.
      Class c = Class.forName(pAnimalType);

      // Now we take the class and give it life. This is as if we did a new to
      // the class with a zero argument constructor.
      // 
      // newInstance always returns an Object, so it needs to be cast to
      // something
      Object aObject = c.newInstance();
      Animal aAnimal = (Animal) aObject;

      // We would usually just do this:
      // Animal aAnimal = (Animal) c.newInstance();

      // For good measure, we are setting the name here.
      aAnimal.setName(pName);

      return aAnimal;
   }

   public static void main(String[] args) throws Exception {

      // If every class has a "class" property, we can use it to get the full
      // class name. Doing this is a little silly, but just shows a way to get
      // a string we can use.
      String aBirdClass = Bird.class.getName();
      Animal aBird = createAnimal(aBirdClass, "Tweety");
      System.out.println(aBird.getName());

      // Now, instead we will supply a path. This is were it get's
      // interesting. Think about where we could get class names from.
      String aFishClass = "net.cyberward.tutorial.oo.reflection.Fish";
      Animal aFish = createAnimal(aFishClass, "Nemo");
      System.out.println(aFish.getName());
   }
}

We have a method here that takes in only two strings, and yet it returns an instance of Animal with its name set. I think that is pretty cool.

First we get a Class object like we have before, using the pAnimalType passed in for the name. Then we get an Object created from the newInstance() method. Now that we have an Object, we can cast it to an Animal, and set the name.

This method isn’t very safe, and many reflective methods like this aren’t. If someone passed in a class name that doesn’t exist, or it can’t be cast to an Animal, we would get errors. Try out ReflectiveTest3, and change the class name of the fish to see what errors we get.

The Factory

We have been slowly working towards the factory pattern. This is really the first pattern that I have presented. (Note: Don’t get too freaked out about patterns. We will talk more about them soon.) This is an established pattern, where the factory creates the appropriate class for the task at hand. Often a factory will create the appropriate objects based on other attributes, like the windowing system (Mac, Windows, Linux), or your current language (English, Spanish, French). For our introduction to the factory, we will simply create Animals based on a full class name. We have done this already under slightly different code, but let‚Äôs put this together into an AnimalFactory.

/**
 * A factory is a helper class that can be used to generate objects of a
 * particular type. It is often used so that you don't need to duplicate a bunch
 * of code throughout your application. It is also used when you don't know what
 * you will create, like in ChildCare and the DataAccessFactory. In that factory
 * we generate objects based on class names in the DataAccessImplementationsXML
 * file.
 * 
 * It is also another good example of the use of an interface. This factory will
 * always return a type Animal. Because of this, the calling class doesn't need
 * to know if the class that is being created is a Bird or Fish. It just
 * understands that the object being returned implements the Animal interface.
 * 
 * @author Chris Ward 
 */
public class AnimalFactory {

   /**
    * Can't get much simpler than this. I call this the manual factory. The
    * object that gets created is hard coded into the method. Other than an
    * example, it is not that helpful. It does show us something though. No
    * matter what type of animal that we create, we always return a type
    * Animal. So, the using class doesn't know or care what type of Animal is
    * created (ie., Bird, Fish, etc) just that it gets an Animal back.
    * 
    * @return
    */
   public static Animal createAnimal() {

      // to return a different animal from the factory, change the line that
      // is used to create an animal
      return new Bird();
      // return new Mammal();
      // return new Fish();
      // return new Butterfly();
   }

   /**
    * This factory returns an Animal just like the manual factory, but this
    * time it does it based on a class name. This allows your code to decide at
    * runtime what type of Animal to return. Again, your code doesn't need to
    * know exactly what the specific Animal has implemented. It just know that
    * it can call the speak() or move() methods on whatever comes back. This is
    * similar to the DataAccessFactory, which gets its name from the XML file.
    * 
    * @param pAnimalType
    *            is full class name of a class that implements the Animal
    *            interface
    * @return
    * @throws ClassNotFoundException
    * @throws IllegalAccessException
    * @throws InstantiationException
    */
   public static Animal createAnimal(String pAnimalType)
         throws ClassNotFoundException, InstantiationException,
         IllegalAccessException {

      // We first get a class from the class name. This is not an instance of
      // the class. This is only the structure and definition.
      Class c = Class.forName(pAnimalType);

      // Now we take the class and give it life. This is as if we did a new to
      // the class with a zero argument constructor.
      Animal aAnimal = (Animal) c.newInstance();

      // You would usually have some initialization code to do in a factory.
      // So for good measure, we call every animal that we create 'Bob'
      aAnimal.setName("Bob");

      return aAnimal;
   }
}

The simplest factory would simply return a particular animal, such as the createAnimal() method does. This way, when ever you decide that you need to deal with a different animal, you would change which line is commented out. That is not very helpful though. It would be much better if took in a String parameter so that the String could tell us what animal to create. See FactoryTest1.

/**
 * Very simple test of the factory. Not doing anything too exciting yet. Just
 * proving that the factory is working how we thought it would.
 * 
 * @author Chris Ward 
 */
public class FactoryTest1 {

   public static void main(String[] args) throws Exception {

      // Our createAnimal method always returns a Bird
      Animal aBird = AnimalFactory.createAnimal();
      System.out.println(aBird.getName());

      // This time, the AnimalFactory doesn't know what to return, other than
      // it is a class that implements the Animal interface. We need to give
      // it the full class name, and it will return an Animal for us.
      Animal aAnimal = AnimalFactory.createAnimal("net.cyberward.tutorial.oo.reflection.Fish");
      System.out.println(aAnimal.getName() + " is a " + aAnimal.getType());
   }
}

Random Animals

/**
 * Little more advanced test of our AnimalFactory. We use this example to show
 * that we can create our objects randomly from an array of strings.
 * 
 * @author Chris Ward 
 */
public class FactoryTest2 {

   /**
    * Now we really prove that you can create any object that implements the
    * Animal interface, and that the calling class doesn't need to know what
    * real object is being returned. In fact, the calling class can't know what
    * object is being created, because we come up with the object randomly.
    */
   public static Animal createRandomAnimal() throws Exception {

      // note: This String array is not a great way to code class names. It is
      // fairly brittle. If the Bird class was moved to another package,
      // without updating this class, this would break. This is just for an
      // example.
      String[] aAnimalArray = new String[5];
      aAnimalArray[0] = "net.cyberward.tutorial.oo.reflection.Bird";
      aAnimalArray[1] = "net.cyberward.tutorial.oo.reflection.Fish";
      aAnimalArray[2] = "net.cyberward.tutorial.oo.reflection.Mammal";

      // We need to first create a Random object that will do the work of
      // getting a random number for us.
      Random aRandom = new Random();

      // This finds a number from 0 to 2
      int aRandomInt = aRandom.nextInt(3);

      // Use our reflection method to create some random Animal
      return AnimalFactory.createAnimal(aAnimalArray[aRandomInt]);
   }

   public static void main(String[] args) throws Exception {
      System.out.println("5 Random Animals named Bob");
      for (int i = 0; i < 5; i++) {
         Animal aAnimal = createRandomAnimal();
         System.out.println(aAnimal.getName() + " is a " + aAnimal.getType());
      }
   }
}

FactoryTest2 shows how using an Array of Strings that represent the Animals, we can get out a Random Animal. Either a Bird, a Fish, or a Mammal. This brings us to the concept we have been working towards.

Changes at Runtime

What would be more powerful is that if at runtime you could specify the animal(s) that you wanted. This is where reflection and the factory really shine. Let’s look at FactoryTest3. We are using the same AnimalFactory, but this time, we are reading in the animals from a file. Let’s pretend that someone, who is not a coder, fills this properties file out with the animals in the zoo. When we run our program, it creates these animals for us.

/**
 * We finally get to a reflection class that looks like a real world example.
 * Lets say we have a very small zoo that can hold 3 animals. We want the
 * ability to create our Animal objects based on what animals are really in the
 * zoo.
 * 
 * With the example, ever time we run, we will look in the properties file for
 * the classes of the three animals that we should create. No need to hard code
 * into the java classes.
 * 
 * @author Chris Ward 
 */
public class FactoryTest3 {

   public static void main(String[] args) throws Exception {

      // Create a properties object that will hold our properties
      Properties properties = new Properties();

      // Get an input stream from the properties file
      InputStream aStream = FactoryTest3.class	.getResourceAsStream("zooAnimals.properties");

      // Load the properties file into the Properties object	
      properties.load(aStream);
      // Ask the factory to create the animal for us from the property with
      // the key of "animal1"
      Animal aAnimal1 = AnimalFactory.createAnimal(properties.getProperty("animal1"));
      System.out.println(aAnimal1.getName() + " is a " + aAnimal1.getType());

      // Ask the factory to create the animal for us from the property with
      // the key of "animal2"
      Animal aAnimal2 = AnimalFactory.createAnimal(properties.getProperty("animal2"));
      System.out.println(aAnimal2.getName() + " is a " + aAnimal2.getType());

      // Ask the factory to create the animal for us from the property with
      // the key of "animal3"
      Animal aAnimal3 = AnimalFactory.createAnimal(properties.getProperty("animal3"));
      System.out.println(aAnimal3.getName() + " is a " + aAnimal3.getType());
   }
}

If you haven’t used property files before, they work pretty simply. Using a structure on a text file of key=value (no quotes, one per line), by convention is called something.properties. When the file is loaded from a stream, the Properties file that is created will be a type of Hashmap that contains key/value pairs that work like any other Hashmap.

So the first three statements create the properties file, and the last asks for a property that is called “animal1”. The value for this is a full class name of an Animal, and our factory can then create one for us as it has in the last couple of examples.

Give FactoryTest3 a try. After compiling it, try changing the animals in the properties file. See how it is able to pick up the changes.

Is creating an instance all we can do?

Not by a long shot. We are only going to look at a few parts of the reflection API here. What we want to look at now is how to call methods. Just like we can create a Class object from a class name, we can create a Method object from a Class, and a method name.

Let’s start with a method that does not take or return any parameters, like speak();

Method aMethod = Bird.class.getMethod("speak", null);
aMethod.invoke(aBird, null);

So, the first thing we do is get a Class, and from that, get the Method that has the name “speak”. Then we can invoke that method. In order to invoke the method, we need an Instance to invoke it on. Remember, speak() is an instance method, so we need an instance of Bird to make this work. See ReflectionTest4 for the full code.

/**
 * Now we need to look at methods. We have seen how you can create an instance
 * of an object with reflection. How about executing methods?
 * 
 * @author Chris Ward 
 */
public class ReflectiveTest4 {

   /**
    * @param args
    * @throws NoSuchMethodException
    * @throws SecurityException
    * @throws InvocationTargetException
    * @throws IllegalAccessException
    * @throws IllegalArgumentException
    */
   public static void main(String[] args) throws SecurityException,
         NoSuchMethodException, IllegalArgumentException,
         IllegalAccessException, InvocationTargetException {

      // We can start with a class we know. We don't need to create a Bird
      // using reflection just to invoke methods using reflection
      Bird aBird = new Bird();

      // First we look at executing a method that returns nothing, and has no
      // parameters, such as speak()

      // We ask for a Method object from the Bird class that is called "speak"
      // and takes no paramater types (the null)
      Method aMethod = Bird.class.getMethod("speak", null);

      // This returns nothing, so we can just invoke it by passing in the
      // instance we want to call the method on, and the parameters we are
      // passing (null for none)
      aMethod.invoke(aBird, null);
   }

Passing in a variable?

ReflectionTest5 has the code for this. This time, we have to pay attention to which method we are looking for. Remember, Java can overload its method names, meaning that the same name can be used many times, if the method signature is different. So, when we ask for the Method, we need to indicate which signature we intend to invoke, by passing in an array of Class types. What are class types? String.class. Animal.class. Integer.class. The method we are going to call is “setName(String pName), so we need to pass in an array of Classes with on String.class element.

/**
 * How about calling methods that take parameters?
 * 
 * @author Chris Ward 
 */
public class ReflectiveTest5 {

   /**
    * @param args
    * @throws NoSuchMethodException
    * @throws SecurityException
    * @throws InvocationTargetException
    * @throws IllegalAccessException
    * @throws IllegalArgumentException
    */
   public static void main(String[] args) throws SecurityException,
         NoSuchMethodException, IllegalArgumentException,
         IllegalAccessException, InvocationTargetException {

      // We can start with a class we know. We don't need to create a Bird
      // using reflection just to invoke methods using reflection
      Bird aBird = new Bird();

      // We are using a method, setName(String pName) that takes in a String
      // type. We could have more than one input parameter, so it is an array.
      Class[] aTypes = { String.class };

      // We ask for a Method object from the Bird class that is called "setName"
      // and takes an Array of parameter types
      Method aMethod = Bird.class.getMethod("setName", aTypes);

      // This returns nothing, so we can now invoke it by passing in the
      // instance we want to call the method on, and the parameters we are
      // passing (the name)
      Object[] aParms = { "Tweety" };
      aMethod.invoke(aBird, aParms);
		
      System.out.println(aBird.getName());
   }
}

Notice that this time when we invoked the method we passed in an array of values. Notice that the array is an array of Objects. The API needs to be flexible enough to take any type of argument, no matter what type, so the array is of Objects. Our only parameter is a String Object, “Tweety”.

Getting out a value?

ReflectionTest6 does this. It is virtually the same as ReflectionTest5. The invoke method that we have been using, actually returns a value : an Object. We can then simply cast this Object to the return type we were expecting.

/**
 * How about calling methods that return values?
 * 
 * @author Chris Ward 
 */
public class ReflectiveTest6 {

   /**
    * @param args
    * @throws NoSuchMethodException
    * @throws SecurityException
    * @throws InvocationTargetException
    * @throws IllegalAccessException
    * @throws IllegalArgumentException
    */
   public static void main(String[] args) throws SecurityException,
         NoSuchMethodException, IllegalArgumentException,
         IllegalAccessException, InvocationTargetException {

      // We can start with a class we know. We don't need to create a Bird
      // using reflection just to invoke methods using reflection
      Bird aBird = new Bird();

      // We ask for a Method object from the Bird class that is called
      // "getName" that takes no parameters, so we can pass null.
      Method aMethod = Bird.class.getMethod("getName", null);

      // This returns something, so we can now invoke it by passing in the
      // instance we want to call the method on, and assign the Object that is
      // returned.
      Object aNameObject = aMethod.invoke(aBird, null);
		
      // We then cast it to the correct return type.
      String aName = (String) aNameObject;
      System.out.println(aName);
   }
}

There is only one invoke method. It does not matter if you are calling it on a Method that takes no parameters, takes several parameters, returns nothing, or returns an Object. The method returns an Object, and takes in the Object to envoke the method on, and an array of Object parameters.

How about constructors with parameters?

We have one thing left to cover. When we talked about constructors, we only talked about the no argument constructor. But what about when we want to call a constructor that takes parameters? It works a lot like passing parameters to methods. This time we deal with Constructor objects. We need to ask the Class for a particular Constructor that matches the supplied array of Class types. For example, if we want to get the Constructor for the Bird that allows us to pass in the name, we need to pass in an array of Class objects, with one String.class in it. Just like when we wanted to call the setName(String pName) method.

Once we have our Constructor object we can get an instance of our Class. With no argument constructors, we called newInstance on the class. Here we need to call newInstance on the Constructor we have, passing in the parameters we want to pass our constructor. This gives us our Object that we need to then cast into a Bird.

/**
 * Now we need to look at constructors. We have seen how you can create an
 * instance of an object with a no argument constructor, but what about if we
 * want to pass in parameters?
 * 
 * @author Chris Ward 
 */
public class ReflectiveTest7 {

   public static void main(String[] args) throws Exception {

      // Every Class, like Bird, or Fish has a Class that represents it's
      // structure. You can access this via the class property.
      Class c = Bird.class;

      // We want the constructor that takes a String name, like this:
      // public Bird(String pName)
      // This is similar to how we asked for Method
      Class[] aTypes = { String.class };
      Constructor aConstructor = c.getConstructor(aTypes);

      // Now that we have the correct constructor, we can invoke this
      // particular constructor, much like we would a Method. This would be
      // the same as: Bird aBird = new Bird("Tweety");
      Object[] aArgs = { "Tweety" };
      Object aBirdObject = aConstructor.newInstance(aArgs);
      Bird aBird = (Bird) aBirdObject;

      // The Bird that we have is just like any Bird that has it's name set.
      System.out.println(aBird.getName());
   }
}

Is that all you can do with reflection?

It is for this lesson, but it is not all you can do with Reflection. You can access fields (variables) directly, even private ones. You can use static fields and methods. You can also use reflection to find out a lot about a class, like what methods it has. Calling constructors and methods are some of the more common things, but there are lots of other possibilities and uses for reflection than what I brought up here.

[drain file 13 show tableRow]

[drain file 14 show tableRow]

Type Download Name Version Size Updated Hits
Series NavigationIntro to Java Abstract ClassesIntro to Java Collections

1 thought on “Intro to Java Reflection

Comments are closed.