CSC/ECE 517 Fall 2010/ch1 1c NR

From Expertiza_Wiki
Jump to navigation Jump to search

Introduction to Reflection

Reflection is a language feature that enables a program to examine itself at runtime and possibly change its behavior accordingly. It was introduced by Brian Cantwell Smith as a framework for language extension.There are two aspects to reflection :

  • Introspection - the ability for a program to observe and reason about its own state.
  • Intercession - the ability for a program to modify its own execution state or alter its own interpretation or meaning.

What is crucial here is that a given program can behave not only as a function, but also as a data structure that can be examined and manipulated to change its behavior. These properties lead to an easily extensible language since the structures used by the language implementation are accessible to the programmer. The programmer can now define programming constructs that would otherwise have been either impossible or extremely difficult to define. These properties have led to the adoption of reflection as a primary means for language extensibility. Reflection is most commonly used in many dynamically typed languages such as Ruby,Smalltalk, Objective-c and scripting languages like Perl,PHP. Statically typed languages such as Java, ML or Haskell also support reflection. Reflective programming languages and platforms provides a comprehensive list of all languages and platforms supporting reflection.


Reflection as Language Feature vs Reflection as Package

For the sake of simplicity, we take Ruby and Java to represent languages that have Reflection as a language feature and reflection as a package respectively.

Reflection in Ruby

One of the languages that has reflection as a built-in language feature is Ruby. Objects in Ruby support reflection by default, hence it is not necessary to use any external or additional libraries. In essence, the programmer does not need to do anything special to start using reflection, it is a native part of the language. Additionally, Ruby's intuitive syntax and ease of use make it possible for someone who is not an expert to write Ruby code that uses reflection. Ruby's reflection functionality is made available by the Object class from which everything in Ruby is derived.

Some of the basic information obtained through reflection is described below:

Class/Superclass Type:

Using the <object>.class will display the name of the class the object belongs to. Whereas <object>.superclass will display the superclass of the object. <object>.ancestors will display the ancestral hierarchy for the object.
Example:

3.class                     # outputs Fixnum
3.class.superclass          # outputs the superclass of 3 which is Fixnum
Fixnum.superclass           # outputs Object class
"hi".class                  # outputs String
Object.ancestors            # outputs an array of Objects ancestors [Object, Kernel, BasicObject] 
3.class.ancestors           # [Fixnum, Integer, Numeric, Comparable, Object, Kernel,BasicObject]

Methods supported by the object

The <object>.method will return all the public methods defined on the object. All the private methods in a class can be seen through <object>.private_methods.
Example:

str = "wikichapter"
str.methods                 # returns an array of all the public methods that can be called on the object
Class.private_method        # returns an array of all private methods in the object Class. 
                              # [:inherited, :initialize, :initialize_copy,...]  

Instance variables,class variables, method access level

We can access the instance variables in a class using <object>.instance_variable. Similarly class variables and constants defined in the class can be accessed via <object>.class_variables.
<object>.private_instance_methods(false) will return all the private instance methods defined in class. If we pass a true as the argument, it will display the private instance methods of its ancestors too. The above Reflection API's are particularly useful when dealing with user defined classes.
Example: This example creates a new class and demonstrates the reflection API's covered so far.

irb(main):105:0> class Example

irb(main):106:1> @@class_var = "hi"
irb(main):107:1> def initialize(x,y)
irb(main):108:2> @a,@b = x,y
irb(main):109:2> end

irb(main):110:1> def method1
irb(main):111:2> puts "In Method1"
irb(main):112:2> end
irb(main):113:1> private:method1

irb(main):114:1> private
irb(main):115:1> def method2
irb(main):116:2> end

irb(main):117:1> protected
irb(main):118:1> def method3
irb(main):119:1> puts "In Method3"
irb(main):120:2> end

irb(main):121:1> public
irb(main):122:1> def method4
irb(main):123:2> end

irb(main):124:1> def method5
irb(main):125:2> end
irb(main):126:1> protected:method5

irb(main):127:1> end
=> Example

Output:


irb(main):131:0> Example.class_variables
=> [:@@class_var]

irb(main):132:0> Example.private_instance_methods
=> [:initialize, :method1, :method2, :default_src_encoding, :irb_binding, :initi
alize_copy, :remove_instance_variable, :sprintf, :format, :Integer, :Float, :Str
ing, :Array, :warn, :raise, :fail, :global_variables, :__method__, :__callee__,
:eval, :local_variables, :iterator?, :block_given?, :catch, :throw, :loop, :call
er, :trace_var, :untrace_var, :at_exit, :syscall, :open, :printf, :print, :putc,
 :puts, :gets, :readline, :select, :readlines, :`, :p, :test, :srand, :rand, :tr
ap, :exec, :fork, :exit!, :system, :spawn, :sleep, :exit, :abort, :load, :requir
e, :require_relative, :autoload, :autoload?, :proc, :lambda, :binding, :set_trac
e_func, :Rational, :Complex, :gem_original_require, :gem, :singleton_method_adde
d, :singleton_method_removed, :singleton_method_undefined, :method_missing]

irb(main):133:0> Example.private_instance_methods(false)
=> [:initialize, :method1, :method2]

irb(main):134:0> Example.public_instance_methods(false)
=> [:method4]

irb(main):135:0> Example.protected_instance_methods(false)
=> [:method3, :method5]

irb(main):140:0> example = Example.new(1,3)
=> #<Example:0x2b8da10 @a=1, @b=3>

irb(main):141:0> example.class
=> Example

irb(main):142:0> example.class.superclass
=> Object

irb(main):143:0> example.class.ancestors
=> [Example, Object, Kernel, BasicObject]

irb(main):145:0> example.methods
=> [:method3, :method4, :method5, :nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :cl
ass, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint,
 :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :
inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :pu
blic_methods, :instance_variables, :instance_variable_get, :instance_variable_se
t, :instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :
public_send, :respond_to?, :respond_to_missing?, :extend, :display, :method, :pu
blic_method, :define_singleton_method, :__id__, :object_id, :to_enum, :enum_for,
 :==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__]

Invoking method dynamically

One way to invoke a method dynamically in ruby is to "send" a message to the object. This is done by calling <class_instance>.send on the instance of the class. The method to be called is passed as the argument to the send API.
Example: For the Example class in the previous section, send can be called

Reflection in Java

Reflection in Java is achieved by making method calls on a Class object. Before using reflection on an object, first it is necessary to map it to an instance of Class. It is important to note that this method only works for types that are derived from Object, not all types in Java are an object, e.g. boolean.

Basic Api in java:

getClass() - for a given instance, an Object type Class is returned.(that instance of type Class which is created for every type of object by JVM which helps to futher get the information about runtime properties of the object and its members.)

Class cls= "Hello World".getClass();

.class - for a given type, object of type Class is returned. (Used when the type is available but no instance.This is also used to obtain the Class for primitive types.)

Class cls = boolean.class;  

Class.forName() - Used when a fully qualified class name is available.

Class cls = Class.forName("[[Ljava.lang.String;");

getName() - for a given instance, the name of the corresponding class is returned.

cls.getName();

getMethods() - for a given Class object,returns list of methods that are not private including inherited methods.

cls.getMethods(); 

getSuperClass() - for a given Class object, returns a list of superclasses for the class.

cls.getSuperClass();

invoke(obj,parameters); - for a given Class object, invokes the method dynamically.

//for some object "obj" of class Abc which has method method_one().
Class cls=obj.getClass();
Method m1 = cls.getMethod(method_one);
m1.invoke(cls,null); // method of instance cls with no parameters(null).

Program using features discussed above:

import java.lang.reflect.*;
public class BasicReflectionFeaturesJava {
		public void Method_1(){
			System.out.println("In Method_1");
		}
		public void Method_2(){
			System.out.println("In Method_2");
		}
		
		public static void main(String args[]) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException{

	       BasicReflectionFeaturesJava obj = new BasicReflectionFeaturesJava(); 

		Class Class_Instance = obj.getClass();           //Get Class type object for BasicReflectionFeaturesJava       	 

		System.out.println("Class Name:" + Class_Instance.getName());	//Get Class Name

		System.out.println("Methods of the Class BasicReflectionFeaturesJava:");
                for(Method m:Class_Instance.getDeclaredMethods()){ //Get all the Declared methods of the class(Doesnot give private methods)
			System.out.println(m.getName());       	  //Get Names of all the methods
		}

		System.out.println("All Methods of Classes in hierarchy from BasicReflectionFeaturesJava to Object:");
		for(Method m:Class_Instance.getDeclaredMethods()){//Get all the Declared methods of the class(Doesnot give private methods)
			System.out.println(m.getName());	  //Get Names of all the methods
		}

		System.out.println("Invoking a method at runtime:");
		Method m = Class_Instance.getMethod("Method_1");  //Get object to a specified method at runtime.
		m.invoke(obj, null);				  //Invoke a specified method at runtime.
	   
	}
	}

The output of the program displays class name, methods declared in it(excluding private), all methods(including derived ones):

Class Name:Excercises.BasicReflectionFeaturesJava

Methods of the Class BasicReflectionFeaturesJava:
Method_1
Method_2
main

All Methods of Classes in hierarchy from BasicReflectionFeaturesJava to Object:
Method_1
Method_2
main

Invoking a method at runtime:
In Method_1

Reflection as Package

Conclusion

References