CSC/ECE 517 Summer 2008/wiki1 3 aobk: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
 
(35 intermediate revisions by 2 users not shown)
Line 1: Line 1:
== Reflection in Ruby and Java ==
== Reflection in Ruby and Java ==
;What is ''Reflection''?
;
Reflection, in the realm of Computer Science, is the process of introspection by a program. Reflection allows program entities to discover things about themselves through introspection.Through the technique of ''metaprogramming'', aspects of a program are examined from within the program itself, which enables the program to modify itself at runtime.   
===What is ''Reflection'' in computer science?===
 
{| border="0" cellpadding="0"
|width="550"|Reflection, in the realm of Computer Science, is the process of introspection by a program. Introspection allows program entities to discover things about themselves. Through the technique of ''metaprogramming'', aspects of a program are examined from within the program itself, which enables the program to modify itself at runtime.  This provides for a very liberated and dynamic programming environment.
|width="50"|
|width="200"|[[Image:linear.png|Reflection]]
|}
 


By looking at and inside classes and objects, much can be discovered, including the following:
By looking at and inside classes and objects, much can be discovered, including the following:
Line 9: Line 16:
* various method information
* various method information


One of the design challenges of using Reflection is that you need to clarify what services you wish to provide at runtime that don't want to implement statically. The provision of reusable services that vary based upon the runtime environment is one of the reasons that frameworks such as Struts and JAAS are created. However, even small applications can face design issues that make a dynamic solution preferable over a static one. A word of caution though, over-reliance on Reflection can lead to performance issues because of the extra overhead of determining what class(es) need to be loaded.
One of the design challenges when using Reflection is that you need to clarify what services you wish to provide at runtime.


=== Java Reflection Sequences ===
=== Java Reflection Sequences ===
Line 27: Line 34:


public class ShowClass {
public class ShowClass {
  /** The main method.  Print info about the named class */
 
/** The main method.  Print info about the named class */
 
   public static void main(String[] args) throws ClassNotFoundException {
   public static void main(String[] args) throws ClassNotFoundException {
     Class c = Class.forName(args[0]);
     Class c = Class.forName(args[0]);
Line 33: Line 42:
   }
   }


  /** Display the modifiers, name, superclass and interfaces of a class
/** Display the modifiers, name, superclass and interfaces of a class
   *  or interface. Then go and list all constructors, fields, and methods. */
   *  or interface. Then go and list all constructors, fields, and methods. */
   public static void print_class(Class c)
   public static void print_class(Class c)
   {
   {
     // Print modifiers, type (class or interface), name and superclass.
     // Print modifiers, type (class or interface), name and superclass.
     if (c.isInterface()) {
     if (c.isInterface()) {
       // The modifiers will include the "interface" keyword here...
       // The modifiers will include the "interface" keyword here...
       System.out.print(Modifier.toString(c.getModifiers()) + " "+c.getName());
       System.out.print(Modifier.toString(c.getModifiers()) + " "+c.getName());
     }
     }
Line 51: Line 64:
          
          
     // Print interfaces or super-interfaces of the class or interface.
     // Print interfaces or super-interfaces of the class or interface.
     Class[] interfaces = c.getInterfaces();
     Class[] interfaces = c.getInterfaces();
     if ((interfaces != null) && (interfaces.length > 0)) {
     if ((interfaces != null) && (interfaces.length > 0)) {
Line 64: Line 78:


     // Now look up and display the members of the class.
     // Now look up and display the members of the class.
     System.out.println(" // Constructors");
     System.out.println(" // Constructors");
     Constructor[] constructors = c.getDeclaredConstructors();
     Constructor[] constructors = c.getDeclaredConstructors();
Line 82: Line 97:
   }
   }


  /** Return the name of an interface or primitive type, handling arrays. */
/** Return the name of an interface or primitive type, handling arrays. */
   public static String typename(Class t) {
    
public static String typename(Class t) {
     String brackets = "";
     String brackets = "";
     while(t.isArray()) {
     while(t.isArray()) {
Line 92: Line 108:
   }
   }


  /** Return a string version of modifiers, handling spaces nicely. */
/** Return a string version of modifiers, handling spaces nicely. */
  public static String modifiers(int m) {
public static String modifiers(int m) {
     if (m == 0) return "";
     if (m == 0) return "";
     else return Modifier.toString(m) + " ";
     else return Modifier.toString(m) + " ";
   }
   }


  /** Print the modifiers, type, and name of a field */
/** Print the modifiers, type, and name of a field */
 
   public static void print_field(Field f) {
   public static void print_field(Field f) {
     System.out.println("  " +
     System.out.println("  " +
Line 108: Line 126:
   *  type of a method or constructor.  Note the use of the Member interface
   *  type of a method or constructor.  Note the use of the Member interface
   *  to allow this method to work with both Method and Constructor objects */
   *  to allow this method to work with both Method and Constructor objects */
   public static void print_method_or_constructor(Member member) {
   public static void print_method_or_constructor(Member member) {
     Class returntype=null, parameters[], exceptions[];
     Class returntype=null, parameters[], exceptions[];
Line 139: Line 158:
</pre>
</pre>


This code is then compiled and run, with the following simple test class passed in:
'''This code is then compiled and run, with the following simple test class passed in:'''
 
<pre>
<pre>
public class testInput {
public class testInput {
Line 153: Line 173:
}
}
</pre>
</pre>
The output generated is as follows:
 
'''The output generated is as follows:'''
 
<pre>
<pre>
public class testInput extends java.lang.Object {
public class testInput extends java.lang.Object {
  // Constructors
   
// Constructors
   public testInput();
   public testInput();
  // Fields
  // Fields
Line 204: Line 227:
irb(main):023:0> Demo.singleton_methods(false)
irb(main):023:0> Demo.singleton_methods(false)
=> ["method5"]
=> ["method5"]
</pre>
In addition to looking inside classes, reflection in Ruby provides the ability to dynamically call methods.  An arbitrary method of some object can be called using <tt>Object#send</tt>, <tt>Method#call</tt>, and also various versions of the <tt>eval</tt> method (including <tt>class_eval</tt>, <tt>module_eval</tt>, and <tt>instance_eval</tt>).
<tt>send</tt> works on any object, and it calls the passed in method on that object.  It will also find new methods added at runtime.  In the below example, the <tt>length</tt> method is called on the object, which is a 14 character string.
<pre>
irb(main):105:0> "This is a test".send(:length)
=> 14
</pre>
<tt>Method</tt> objects represent a chunk of code and a context in which it executes.  The <tt>Method</tt> object is created, and it can be executed whenever desired by sending it the <tt>call</tt> message:
<pre>
irb(main):106:0> test_message = "Test message".method(:length)
=> #<Method: String#length>
irb(main):107:0> test_message.call
=> 12
</pre>
A third way methods can be invoked dynamically using reflection is with the <tt>eval</tt> method.  <tt>eval</tt> provides the ability to parse and execute an arbitrary string of legal Ruby source code.  The string of code can be stored as an object, and then evaluated at a later time.
<pre>
irb(main):111:0> eval_test = %q{"A third test".length}
=> "\"A third test\".length"
irb(main):112:0> eval eval_test 
=> 12
</pre>
</pre>


Line 209: Line 254:


*By coding and comparing we find that Reflection in Java tends to be more verbose than Ruby code which accomplishes the same task.  Also, exceptions such as <tt>NoSuchMethodError</tt> and <tt>InvocationTargetException</tt> tend to create further complications and confusion when using Java's reflection API.
*By coding and comparing we find that Reflection in Java tends to be more verbose than Ruby code which accomplishes the same task.  Also, exceptions such as <tt>NoSuchMethodError</tt> and <tt>InvocationTargetException</tt> tend to create further complications and confusion when using Java's reflection API.
**In order to print out all the methods contained in a class in Java, we approached 100 lines of code.  It took merely 4 lines of code in Ruby to print all private, protected, public, and singleton methods of a class.  This illustrates Ruby efficiency when looking inside classes.


*Reflection API in java is also used in Database Access with SQL.
*Language facilities such as ''aliasing'', ''reflection'', and ''metaprogramming'' make Aspect Oriented Programming much easier to perform in Ruby than in Java.
 
*In Ruby, Aspect Oriented Programming(AOP) can be performed using ''aliasing, reflection and metaprogramming''.  These language facilities make it much easier to perform AOP than in Java.


=== Uses of Reflection ===
=== Uses of Reflection ===
Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine. This is a relatively advanced feature and should be used only by developers who have a strong grasp of the fundamentals of the language. With that caveat in mind, reflection is a powerful technique and can enable applications to perform operations which would otherwise be impossible.  
Reflection is used by programs which require the ability to analyze or modify the runtime behavior of applications.This is a relatively advanced feature.Thus, reflection is a powerful technique which can enable applications to perform operations which would otherwise be impossible.


*Extensibility Features  
*Extensibility Features:
An application may make use of external, user-defined classes by creating instances of extensibility objects using their fully-qualified names.  
An application may utilize external, user-defined classes by creating instances of extensibility objects using their fully-qualified names.  


*Class Browsers and Visual Development Environments  
*Class Browsers and Visual Development Environments  
A class browser needs to be able to enumerate the members of classes. Visual development environments can benefit from making use of type information available in reflection to aid the developer in writing correct code.  
A class browser could enumerate the members of classes. Visual development environments can benefit from making use of type information available in reflection to aid the developer in writing correct code.  


*Debuggers and Test Tools  
*Debuggers and Test Tools  
Debuggers need to be able to examine private members on classes. Test harnesses can make use of reflection to systematically call a discoverable set APIs defined on a class, to insure a high level of code coverage in a test suite.
Debuggers could examine private members on classes. Test harnesses could use reflection to systematically call a discoverable set APIs defined on a class, to insure a high level of code coverage in a test suite.
 
*Reflection API in java is also used in Database Access with SQL.


=== Drawbacks of Reflection ===
=== Drawbacks of Reflection ===
Reflection is powerful, but should not be used indiscriminately. If it is possible to perform an operation without using reflection, then it is preferable to avoid using it. The following concerns should be kept in mind when accessing code via reflection.
Reflection is powerful, but should not be used indiscriminately. It is preferable to avoid using reflection, if possible. The following concerns should be kept in mind when accessing code via reflection:


*Performance Overhead  
*Performance Overhead  
Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications.  
Certain Java virtual machine optimizations, which greatly increase performance in Java applications, cannot be performed when reflective operations are used due to the dynamically resolved types involved in reflection.  Therefore, reflection should be avoided in sections of code called frequently in performance-sensitive applications.


*Security Restrictions  
*Security Restrictions  
Reflection requires a runtime permission which may not be present when running under a security manager. This is in an important consideration for code which has to run in a restricted security context, such as in an Applet.  
Reflection requires a runtime permission which may not be present when running under a security manager. This is important for code running in a restricted security contect, e.g. in an Applet.


*Exposure of Internals  
*Exposure of Internals
Since reflection allows code to perform operations that would be illegal in non-reflective code, such as accessing private fields and methods, the use of reflection can result in unexpected side-effects, which may render code dysfunctional and may destroy portability. Reflective code breaks abstractions and therefore may change behavior with upgrades of the platform.
Operations which would be illegal in a non-reflective environment, such as accessing private fields and methods, are allowed in reflective code.  Consequently, unexpected side-effects, which may render code dysfunctional and destroy portability, may result from the use of reflection. Abstractions may also be broken, thus changing behavior with platform upgrades.


=== ''Refrences'' ===
=== ''References'' ===
#Flanagan, David.  [http://books.google.com/books?id=kADTIsgeLh4C Java Examples in a Nutshell].  O'Reilly, 2004.
#Flanagan, David.  [http://books.google.com/books?id=kADTIsgeLh4C Java Examples in a Nutshell].  O'Reilly, 2004.
#Thomas, Dave.  [http://books.google.com/books?id=8LXOAAAACAAJ Programming Ruby].  Raleigh: Pragmatic Bookshelf, 2004.
#Thomas, Dave.  [http://books.google.com/books?id=8LXOAAAACAAJ Programming Ruby].  Raleigh: Pragmatic Bookshelf, 2004.


=== ''External Links ===
=== ''External Links'' ===
*http://phrogz.net/programmingruby/ospace.html
*http://phrogz.net/programmingruby/ospace.html
*http://java.sun.com/docs/books/tutorial/reflect/
*http://java.sun.com/docs/books/tutorial/reflect/
*http://java.sun.com/developer/technicalArticles/ALT/Reflection/index.html
*http://java.sun.com/developer/technicalArticles/ALT/Reflection/index.html
*http://www.javaworld.com/javaworld/jw-07-2006/jw-0717-ruby.html

Latest revision as of 03:02, 12 June 2008

Reflection in Ruby and Java

What is Reflection in computer science?

Reflection, in the realm of Computer Science, is the process of introspection by a program. Introspection allows program entities to discover things about themselves. Through the technique of metaprogramming, aspects of a program are examined from within the program itself, which enables the program to modify itself at runtime. This provides for a very liberated and dynamic programming environment. Reflection


By looking at and inside classes and objects, much can be discovered, including the following:

  • what objects are contained in a program
  • the class hierarchy
  • the attributes and methods of the objects contained in the program
  • various method information

One of the design challenges when using Reflection is that you need to clarify what services you wish to provide at runtime.

Java Reflection Sequences

Reflection in Java
Java uses a Reflection API to inspect and manipulate itself. It includes the expanded class class in java.lang and the java.lang.reflect package, which represents the members of a class with Method, Constructor and Field objects.Reflection can be used to obtain information about a class and its members.


Example:

  • This example uses the Class.forName() method to dynamically load the named class, and then calls various methods of the class object to look up the Superclass, Interfaces, and Members of the class. The example uses Constructor, Field and Method objects to obtain information about each member of the class.


import java.lang.reflect.*;

/** A program that displays a class synopsis for the named class */

public class ShowClass {

/** The main method.  Print info about the named class */

  public static void main(String[] args) throws ClassNotFoundException {
    Class c = Class.forName(args[0]);
    print_class(c);
  }

/** Display the modifiers, name, superclass and interfaces of a class
   *  or interface. Then go and list all constructors, fields, and methods. */

  public static void print_class(Class c)
  {
    // Print modifiers, type (class or interface), name and superclass.

    if (c.isInterface()) {

      // The modifiers will include the "interface" keyword here...

      System.out.print(Modifier.toString(c.getModifiers()) + " "+c.getName());
    }
    else if (c.getSuperclass() != null)
      System.out.print(Modifier.toString(c.getModifiers()) + " class " +
                       c.getName() +
                       " extends " + c.getSuperclass().getName());
    else
      System.out.print(Modifier.toString(c.getModifiers()) + " class " +
                       c.getName());
        
    // Print interfaces or super-interfaces of the class or interface.

    Class[] interfaces = c.getInterfaces();
    if ((interfaces != null) && (interfaces.length > 0)) {
      if (c.isInterface()) System.out.println(" extends ");
      else System.out.print(" implements ");
      for(int i = 0; i < interfaces.length; i++) {
        if (i > 0) System.out.print(", ");
        System.out.print(interfaces[i].getName());
      }
    }

    System.out.println(" {");            // Begin class member listing.

    // Now look up and display the members of the class.

    System.out.println(" // Constructors");
    Constructor[] constructors = c.getDeclaredConstructors();
    for(int i = 0; i < constructors.length; i++)      // Display constructors.
      print_method_or_constructor(constructors[i]);

    System.out.println(" // Fields");
    Field[] fields = c.getDeclaredFields();           // Look up fields.
    for(int i = 0; i < fields.length; i++)            // Display them.
      print_field(fields[i]);

    System.out.println(" // Methods");
    Method[] methods = c.getDeclaredMethods();        // Look up methods.
    for(int i = 0; i < methods.length; i++)           // Display them.
      print_method_or_constructor(methods[i]);

    System.out.println("}");             // End class member listing.
  }

/** Return the name of an interface or primitive type, handling arrays. */
  
public static String typename(Class t) {
    String brackets = "";
    while(t.isArray()) {
      brackets += "[]";
      t = t.getComponentType();
    }
    return t.getName() + brackets;
  }

/** Return a string version of modifiers, handling spaces nicely. */
 
 public static String modifiers(int m) {
    if (m == 0) return "";
    else return Modifier.toString(m) + " ";
  }

/** Print the modifiers, type, and name of a field */

  public static void print_field(Field f) {
    System.out.println("  " +
                       modifiers(f.getModifiers()) +
                       typename(f.getType()) + " " + f.getName() + ";");
  }

  /** Print the modifiers, return type, name, parameter types and exception
   *  type of a method or constructor.  Note the use of the Member interface
   *  to allow this method to work with both Method and Constructor objects */

  public static void print_method_or_constructor(Member member) {
    Class returntype=null, parameters[], exceptions[];
    if (member instanceof Method) {
      Method m = (Method) member;
      returntype = m.getReturnType();
      parameters = m.getParameterTypes();
      exceptions = m.getExceptionTypes();
    } else {
      Constructor c = (Constructor) member;
      parameters = c.getParameterTypes();
      exceptions = c.getExceptionTypes();
    }

    System.out.print("  " + modifiers(member.getModifiers()) +
                     ((returntype!=null)? typename(returntype)+" " : "") +
                     member.getName() + "(");
    for(int i = 0; i < parameters.length; i++) {
      if (i > 0) System.out.print(", ");
      System.out.print(typename(parameters[i]));
    }
    System.out.print(")");
    if (exceptions.length > 0) System.out.print(" throws ");
    for(int i = 0; i < exceptions.length; i++) {
      if (i > 0) System.out.print(", ");
      System.out.print(typename(exceptions[i]));
    }
    System.out.println(";");
  }
}

This code is then compiled and run, with the following simple test class passed in:

public class testInput {
    public static void main(String[] args) {}
    public int method1() {
        return 0;
    }
    public String method2() {
        return "";
    }
    public void method3() {}
    public void method4(int num) {}
}

The output generated is as follows:

public class testInput extends java.lang.Object {
 
// Constructors
  public testInput();
 // Fields
 // Methods
  public int method1();
  public java.lang.String method2();
  public void method3();
  public void method4(int);
  public static void main(java.lang.String[]);
}

Ruby Reflection Sequences

The Java code above, which displays constructors, methods, and other information pertinent to the testInput class, is somewhat convoluted and difficult to trace. In Ruby, the methods below can be called on a module/class to return similar information:

  • mod.private_instance_methods(inc_super=true)
  • mod.protected_instance_methods(inc_super=true)
  • mod.public_instance_methods(inc_super=true)
  • mod.singleton_methods(inc_super=true)

For example, using the Demo class below, all of the class' methods (private, protected, public, and singleton) can be easily displayed.

irb(main):001:0> class Demo
irb(main):002:1>   public
irb(main):003:1>     def method1
irb(main):004:2>   end
irb(main):005:1>   private
irb(main):006:1>     def method2
irb(main):007:2>   end
irb(main):008:1>   public
irb(main):009:1>     def method3
irb(main):010:2>   end
irb(main):011:1>   protected
irb(main):012:1>     def method4
irb(main):013:2>   end
irb(main):014:1>   def Demo.method5
irb(main):015:2>   end
irb(main):016:1>   public
irb(main):017:1>     def method6
irb(main):018:2>   end
irb(main):019:1> end
=> nil
irb(main):020:0> Demo.private_instance_methods(false)
=> ["method2"]
irb(main):021:0> Demo.protected_instance_methods(false)
=> ["method4"]
irb(main):022:0> Demo.public_instance_methods(false)
=> ["method3", "method6", "method1"]
irb(main):023:0> Demo.singleton_methods(false)
=> ["method5"]

In addition to looking inside classes, reflection in Ruby provides the ability to dynamically call methods. An arbitrary method of some object can be called using Object#send, Method#call, and also various versions of the eval method (including class_eval, module_eval, and instance_eval).

send works on any object, and it calls the passed in method on that object. It will also find new methods added at runtime. In the below example, the length method is called on the object, which is a 14 character string.

irb(main):105:0> "This is a test".send(:length)
=> 14

Method objects represent a chunk of code and a context in which it executes. The Method object is created, and it can be executed whenever desired by sending it the call message:

irb(main):106:0> test_message = "Test message".method(:length)
=> #<Method: String#length>
irb(main):107:0> test_message.call
=> 12

A third way methods can be invoked dynamically using reflection is with the eval method. eval provides the ability to parse and execute an arbitrary string of legal Ruby source code. The string of code can be stored as an object, and then evaluated at a later time.

irb(main):111:0> eval_test = %q{"A third test".length}
=> "\"A third test\".length"
irb(main):112:0> eval eval_test  
=> 12

Ruby Reflection vs. Java Reflection

  • By coding and comparing we find that Reflection in Java tends to be more verbose than Ruby code which accomplishes the same task. Also, exceptions such as NoSuchMethodError and InvocationTargetException tend to create further complications and confusion when using Java's reflection API.
    • In order to print out all the methods contained in a class in Java, we approached 100 lines of code. It took merely 4 lines of code in Ruby to print all private, protected, public, and singleton methods of a class. This illustrates Ruby efficiency when looking inside classes.
  • Language facilities such as aliasing, reflection, and metaprogramming make Aspect Oriented Programming much easier to perform in Ruby than in Java.

Uses of Reflection

Reflection is used by programs which require the ability to analyze or modify the runtime behavior of applications.This is a relatively advanced feature.Thus, reflection is a powerful technique which can enable applications to perform operations which would otherwise be impossible.

  • Extensibility Features:

An application may utilize external, user-defined classes by creating instances of extensibility objects using their fully-qualified names.

  • Class Browsers and Visual Development Environments

A class browser could enumerate the members of classes. Visual development environments can benefit from making use of type information available in reflection to aid the developer in writing correct code.

  • Debuggers and Test Tools

Debuggers could examine private members on classes. Test harnesses could use reflection to systematically call a discoverable set APIs defined on a class, to insure a high level of code coverage in a test suite.

  • Reflection API in java is also used in Database Access with SQL.

Drawbacks of Reflection

Reflection is powerful, but should not be used indiscriminately. It is preferable to avoid using reflection, if possible. The following concerns should be kept in mind when accessing code via reflection:

  • Performance Overhead

Certain Java virtual machine optimizations, which greatly increase performance in Java applications, cannot be performed when reflective operations are used due to the dynamically resolved types involved in reflection. Therefore, reflection should be avoided in sections of code called frequently in performance-sensitive applications.

  • Security Restrictions

Reflection requires a runtime permission which may not be present when running under a security manager. This is important for code running in a restricted security contect, e.g. in an Applet.

  • Exposure of Internals

Operations which would be illegal in a non-reflective environment, such as accessing private fields and methods, are allowed in reflective code. Consequently, unexpected side-effects, which may render code dysfunctional and destroy portability, may result from the use of reflection. Abstractions may also be broken, thus changing behavior with platform upgrades.

References

  1. Flanagan, David. Java Examples in a Nutshell. O'Reilly, 2004.
  2. Thomas, Dave. Programming Ruby. Raleigh: Pragmatic Bookshelf, 2004.

External Links