CSC/ECE 517 Summer 2008/wiki1 3 ref
Introduction
Reflection is built into Ruby, but in Java, it's a special API. Does this make Ruby code easier to write than Java code? Give examples of reflection sequences in both languages, and analyze which is clearer, and also, if possible, which is more efficient.
What is Reflection?
Reflection is the a programming paradigm that allows a program to examine it's own code, and modify itself or execute code generated dynamically. The definition of Reflection from the [Wikipedia Reflection article] says "Reflection is the process by which a computer program can observe and modify its own structure and behavior." It provides a way for a program to change it's behavior and state based on it's current behavior and state.
In the paper [An Introduction to Reflection-Oriented Programming], it is explained with a real-world analogy:
The concept of reflection is best understood by reaching back to the study of self-awareness in artificial intelligence: "Here I am walking down the street in the rain. Since I'm starting to get drenched, I should open my umbrella." This thought fragment reveals a self-awareness of behavior and state, one that leads to a change in that selfsame behavior and state. It would be desirable for computations to avail themselves of these reflective capabilities, examining themselves in order to make use of meta-level information in decisions about what to do next.
Ruby Reflection
In Ruby, metaprogramming is an important feature. Because it is also a dynamically typed language, it's very useful to be able to examine the type of a given object at runtime, since it's type is not always known at compile time. Thus, a mechanism to change the program's behavior at runtime is needed. This is the reasoning behind Ruby's strong support of reflection. Ruby allows a program to examine the properties of any given object to determine it's supported methods, members, etc. It also provides mechanisms to examine the class structure of a method, including it's class heirarchy and meta information such as access modifiers.
Since objects in Ruby are dynamically typed, any method can be called on any object. Because the compiler doesn't impose restrictions on types, the runtime doesn't either, so Ruby code is able to call methods on objects dynamically at runtime.
Java Reflection API
In Java, reflection capabilities are available, but they are not built into the language. The java.lang.reflect package provides the API for reflection, along with the another important class, java.lang.Class. [Metaprogramming] is writing code that writes and executes other code at runtime, and reflection is one way to implement that. Since metaprogramming is not a feature or Java, reflection in Java typically requires more code than Ruby does, and thus is not as easy to use. It is also more limited than Ruby's reflection capabilities, as demonstrated below.
Java Limitations
Something Ruby can do that Java can't is to iterate over the collection all of the objects of a certain type or simply all objects that exist in the runtime. The ObjectSpace class in Ruby has a method each_object(), which iterates over each object that matches the type of it's parameter. This example from [Programming Ruby: The Pragmatic Programmer's Guide] illustrates it's use.
a = 102.7 b = 95.1 ObjectSpace.each_object(Numeric) {|x| p x }
This prints out the value of each Numeric type object that exists in the Ruby environment. Java's reflection API doesn't provide a mechanism to iterate over Objects that you don't already have a reference to. While this functionality could be provided by an extension to the JVM or a third party library, in Ruby, it is built into the language from the ground up.
Ruby v. Java Examples
Here are some examples of Java and Ruby code that demonstrate how each language accomplishes the same task using it's reflection capabilities.
List methods of a class - Java
In java, to print the names of all the methods an Object[] has, the following code is needed:
Object[] objArray = new Object[]{"Hello World"}; Method[] methods = objArray.getClass().getMethods(); for (Method m : methods) { System.out.println(m.getName()); }
List methods of a class - Ruby
In Ruby, the same functionality is a single line:
puts Array.methods
Invoke method on an instance - Java
In Java, the indexOf(String) method is used to find the index of a substring in a given String
"Hello World".indexOf("W");
To use reflection to make this method call requires the following code:
String s = "Hello World"; Method method; try { method = s.getClass().getMethod("indexOf", new Class[]{String.class}); Object result = method.invoke(s, "W"); System.out.println(result); } catch(NoSuchMethodException nsme) { nsme.printStackTrace(); } catch(IllegalAccessException iae) { iae.printStackTrace(); } catch(InvocationTargetException ite) { ite.printStackTrace(); }
As you can see, this requires creating arrays of Class objects to specify parameter types, and possibly casting the result of the method call from Object to the correct type. It also requires you to catch 3 possible Exceptions that may occur. Ruby's reflections mechanisms don't require exception handling.
Invoke method on an instance - Ruby
In Ruby, the same method call is:
"Hello World".index("W")
Using reflection, this is reduced to a simple 3 lines of code
s = "Hello World" method = s.method("index") method.call("W")