Upload
others
View
1
Download
0
Embed Size (px)
Citation preview
14/04/15
1
Run-Time Type Identification and Reflection
Advanced Programming
14/04/15 Barbara Russo
Motivation
• Since all Java classes are derived from Object, it’s easy to mix objects of different types together into a collection
• The retrieving objects from collections creates problem, because the actual type of the objects is lost
• Solution: RTTI is used to reveal the true types at run time
14/04/15 Barbara Russo
14/04/15
2
RTTI
• RTTI lets you find the exact type of an object when you only have a reference to the base type
14/04/15 Barbara Russo
Collections in Java • Array
– has a special language support
• Iterators
– Iterator(I)
• Collections also called containers
– Collection(I)
– Set(I)
• HashSet(c), TreeSet(c)
– List(I)
• ArrayList(c), LinkedList(c)
– Map(I)
• HashMap(c), TreeMap(c)
14/04/15 Barbara Russo
14/04/15
3
The collections interfaces
14/04/15 5
RTTI with polymorphism
• Consider the class hierarchy that uses polymorphism.
• The generic type is the base class Shape, and the specific derived types are Circle, Square, and Triangle:
14/04/15 Barbara Russo
14/04/15
4
Goal of RTTI
• You can manipulate references to the base type (Shape, in this case) and
• when extending the program by adding a new class (Triangle, derived from Shape, for example), the original code is not affected
14/04/15 Barbara Russo
RTTI examples
• Casting
• instanceOf operator
14/04/15 Barbara Russo
14/04/15
5
Casting • import java.util.*; • class Shape { • void draw() { • System.out.println(this + ".draw()"); • } • } • class Circle extends Shape { • public String toString() { return "Circle"; } • } • class Square extends Shape { • public String toString() { return "Square"; } • } • class Triangle extends Shape { • public String toString() { return "Triangle"; } • }
14/04/15 Barbara Russo
Example: casting at run time public class Shapes {
public static void main(String[] args) {
ArrayList s = new ArrayList();
s.add(new Circle());
s.add(new Square());
s.add(new Triangle());
Iterator e = s.iterator();
while(e.hasNext()) (Shape)e.next()).draw();
}
} 14/04/15 Barbara Russo
I just need to cast for the base, then polymorphism applies
e.next() returns a type Object : Object x=newCircle() Down casting here is safe as there is no assignement
14/04/15
6
Interpreting the example
• The class Shape contains draw( ) that indirectly uses toString( ) to print an identifier for the class by passing this to System.out.println( )
• If that function sees an object, it automatically calls the toString( ) method to produce a String representation.
• Each of the derived classes overrides the toString( ) method (from Object) so that draw( ) ends up printing something different in each case
14/04/15 Barbara Russo
Interpreting the example • In main( ), specific types of Shape are created and then added to an
ArrayList. This is the point at which the downcast occurs because the ArrayList holds only Object references
• when we access the ArraList we just get Object: we have lost the specializations of the objects in the array
• Therefore next( ) naturally produces an Object reference (Object x)
• So a cast to Shape is necessary
• Down casting is the first example of RTTI, since in Java all casts are checked for correctness at run-time!
14/04/15 Barbara Russo
14/04/15
7
Interpreting the example • In
((Shape)e.next()).draw();
• we mean the next object is referenced by a reference variable of type Shape:
Shape aShape
• Then we pass an object of the children type at run time (the entry in the array)
Shape aShape = new Circle();
• We have manipulated the base with casting and we have used polymorphism with the instantiation above
14/04/15 Barbara Russo
Note on casting types • (Downcasting) A conversion from type Object to type Shape requires a run-
time check to make sure that the run-time value is actually an instance of class Shape or one of its subclasses; if it is not, an exception is thrown.
Shape aShape= (Shape) getDog();
– if the returning object of getDog() is not a subtype of Shape, this throws an exception
• (Upcasting) A conversion from type Shape to type Object requires no run-time action; Shape is a subclass of Object, so any reference produced by an expression of type Shape is a valid reference value of type Object. In this case, the reference variable points to the internal object of the object of Shape of type Object
14/04/15 Barbara Russo
14/04/15
8
RTTI with
• instanceof operator
14/04/15 Barbara Russo
Instanceof • The operator instanceof is applied to recognize the type of the object passed
as reference
– instanceof compares an object to a specified type and returns true or false.
• In inheritance, as derived classes (Circle and Triangle) are a base class (Shape), the method can be applied without any casting
• returns false when given a null value or the object is not of a given class
14/04/15 Barbara Russo
14/04/15
9
Example: instanceof() class InstanceofTest { public static void main(String[] args) { Shape obj1 = new Shape(); Shape obj2 = new Circle(); System.out.println("obj1 instanceof Shape: " + (obj1 instanceof Shape));
System.out.println("obj1 instanceof Circle: " + (obj1 instanceof Circle)); System.out.println("obj1 instanceof MyInterface: " + (obj1 instanceof MyInterface)); System.out.println("obj2 instanceof Shape: " + (obj2 instanceof Shape)); System.out.println("obj2 instanceof Circle: " + (obj2 instanceof Circle)); System.out.println("obj2 instanceof MyInterface: " + (obj2 instanceof MyInterface));
} } class Shape{} class Circle extends Shape implements MyInterface{} interface MyInterface{}
14/04/15 Barbara Russo
instanceOf() Output:
obj1 instanceof Shape: true
obj1 instanceof Circle: false
obj1 instanceof MyInterface: false
obj2 instanceof Shape: true
obj2 instanceof Circle: true
obj2 instanceof MyInterface: true
14/04/15 Barbara Russo
Circle is a child of Shape so if I ask whether it is of type Shape I get true
14/04/15
10
instanceof
• Avoid to use the instanceOf operator to call a method based explicitly on the class of some object, instead of implicitly using an overridden method and polymorphism
14/04/15 Barbara Russo
do not use instanceof instead of polymorphism! class Shape { void draw() { System.out.println(“Shape”); } } class Circle extends Shape { public String drawCircle() {System.out.println("Circle”); } } class Square extends Shape { public String drawSquare() {System.out.println(“Square”); } } public static void draw(Shape o){ if (o instanceof Circle){ Circle circle = (Circle) o; o.drawCircle();} else if (o instanceof Square){ Square circle = (Square) o; o.drawSquare();} }
14/04/15 Barbara Russo
14/04/15
11
Accessing Class objects // static method of Class (at run time):
Class circle = Class.forName(“Circle“);
// class literal (at compile time):
Class rectangle = Square.class;
// method of Object (at run time):
Class x = x.getClass();
• Class literals are more efficient since they are checked at compile time.
• With getClass() you have dynamic access to the unknown type of an object.
14/04/15 Barbara Russo
Example in Java (1/4)
14/04/15 Barbara Russo
public class Animal {
private String name; public Animal(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return "<" + name + ">"; }
} public class Cat extends Animal {
public Cat(String name) { super(name); }
} public class Dog extends Animal { public Dog(String name) {
super(name); }
}
14/04/15
12
Example in Java (2/4)
14/04/15 Barbara Russo
import java.util.*; public class BagOfObjects {
private ArrayList listOfObjects = new ArrayList(); private int noCats = 0; private int noDogs = 0; private int noOthers = 0; public void add(Object o) { if (o instanceof Cat) noCats++; else if (o instanceof Dog) noDogs++; else noOthers++; listOfObjects.add(o); }
// continue …
Using the operator instanceof we detect the true type of the object being added to the bag. This is fine as we are using instanceof explicitly and not with method overriding
Example in Java (3/4)
14/04/15 Barbara Russo
// continued class BagOfObjects…
public String toString() { String res = "In total there are: \n"; res += " " + noCats + " cats,\n"; res += " " + noDogs + " dogs, and\n"; res += " " + noOthers + " others\n\n"; for (int i = 0; i < listOfObjects.size(); i++) { Object t = listOfObjects.get(i); res += "\tElement " + i + " is an object of " + t.getClass(); if (t instanceof Dog || t instanceof Cat) { Animal a = (Animal) t; res += "\t --- Name: " + a.getName() + " \n "; } else res += "\n"; } return res; }
}
Here we use the method getClass(), inherited from Object, which indirectly accesses the class object of t in order to display its class name at run time
We check at run time the type of t, then we downcast it to a reference to Animal in a safe way as t still remains a reference to an Object . Then we apply polymorphism to get the right name of the children at run time. Again the use of instanceof is explicit
14/04/15
13
Example in Java (4/4)
14/04/15 Barbara Russo
public class BagTest {
public static void main(String argv[]) { BagOfObjects aBag = new BagOfObjects(); aBag.add(new Object()); aBag.add(new Cat("Fuffy")); aBag.add(new Cat(“Garfield")); aBag.add(new Dog("Fido")); System.out.println(aBag); }
}
Interpreting the example
• The method toString() of Object is overridden by the toString() of BagOfObjects and the console displays the nested information returned with the String “res”
14/04/15 Barbara Russo
14/04/15
14
RTTI vs reflection API
• With RTTI we need to have all the types we use at run time available at compile time
• Many times this is not possible
– if we receive data that represents classes remotely. At compile time we do not have them • e.g. with Remote Method Invocation (RMI) we can call methods
distributed on remote machines. As such locally at compile time we do not know the types used by these methods
14/04/15 Barbara Russo
The Reflection API
• To solve this issue we use the reflection API
• Objects of the java.lang.reflect package are created by the JVM at run time to represent the corresponding member of the class / interface unknown at compilation time (we need to have the .class files available only at run time)
14/04/15 Barbara Russo
14/04/15
15
Extending RTTI: use of reflection
• In the above example, the down cast is checked at run time and at run time we can call the appropriate method of the child
• Sometimes we want to know the exact type of the reference for this we use reflection
14/04/15 Barbara Russo
Java reflection API
• The reflection API reflects, the classes, interfaces, and objects accessible by the current JVM
14/04/15 Barbara Russo
14/04/15
16
Reflection API
• The reflection API comprises the
• java.lang.reflect package and
• java.lang.Class
14/04/15 Barbara Russo
Use of API reflection • The reflection API represents, or reflects, the classes,
interfaces, and objects in the current Java Virtual Machine
• Use the reflection API if you are writing development tools such as debuggers, class browsers, and GUI builders
• Be careful to use it when you have security issues
http://docs.oracle.com/javase/tutorial/reflect/
14/04/15 Barbara Russo
14/04/15
17
With the reflection API you can ü Determine the class of an object
ü Get information about a class's modifiers, fields, methods, constructors, and superclasses
ü Find out what constants and method declarations belong to an interface
ü Create an instance of a class whose name is not known until runtime
ü Get and set the value of an object's field, even if the field name is unknown to your program until runtime
ü Invoke a method on an object, even if the method is not known until runtime
ü Create a new array, whose size and component type are not known until runtime, and then modify the array's components
14/04/15 Barbara Russo
java.lang.Class http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html
• Instances of the class Class represent classes and interfaces in a running Java application – There is a Class object for each class or interface
– Every array is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions
– The primitive Java types (boolean, byte, char, short, int, long, float, and double), and the keyword void are also represented as Class objects
14/04/15 Barbara Russo
14/04/15
18
Understanding Class • Every time we write and compile a new class one
object of Class is created. It is saved in a file .class and called with the same name of the class
• At run time when we want to create a given object of the class, the JVM checks first if the object of Class has been loaded
• If not the JVM loads it searching for the file .class with the same name of the class
14/04/15 Barbara Russo
Example: forName()
• The static method forName(String s) of Class receives a string with the exact name of a class and returns a reference to the object of Class for that type
• Class.forName(“Shape”); returns a reference to the object of Class of type Shape
14/04/15 Barbara Russo
14/04/15
19
14/04/15 CASE 37
import java.util.*; public final class Sample {
private String fName; public Sample(String fName){ super(); this.fName = fName; } public String toString() { StringBuffer result = new StringBuffer(); String newLine = System.getProperty("line.separator"); // returns the line separator result.append( this.getClass().getName() ); result.append( " Object {" ); result.append(newLine); java.lang.reflect.Field[] fields = this.getClass().getDeclaredFields(); for ( int fieldIdx=0; fieldIdx < fields.length; ++fieldIdx ) { result.append(" "); try { result.append( fields[fieldIdx].getName() ); // returns the String value of the field: “fName” result.append(": "); result.append( fields[fieldIdx].get(this) ); // it returns the value of the Field of the object of type Sample :“CIAO”…
Exercise
Exercise cont.
… } catch ( IllegalAccessException ex ) { System.out.println(ex);
} result.append(newLine); } result.append("}"); return result.toString();
}
public static void main ( String[] arguments ) { Sample sample = new Sample( “CIAO"); System.out.println(sample); }
}
14/04/15 CASE 38 Write the output!
14/04/15
20
Solution Sample Object {
fName: CIAO
}
14/04/15 CASE 39
Example with reflection
• Bag of objects using reflection
• Consider the aforementioned bag of object example
• We change the BagOfObjects class implementation and add an helper class ClassCount in order to use reflection
14/04/15 Barbara Russo
14/04/15
21
Example
14/04/15 Barbara Russo
class ClassCount { // helper class to keep track of occurrences
private Class classOfObjects; private int occurrences;
Class getClassOfObjects() { return classOfObjects; } void setClassOfObjects(Class v) { this.classOfObjects = v; } int getOccurrences() { return occurrences; } void setOccurrences(int v) { this.occurrences = v; } void incrementOccurrences() { occurrences++; }
ClassCount(Class c, int n) { classOfObjects = c; occurrences = n; }
}
The class and all its methods have default access level, because we don’t need to use them outside the package
Example in Java (1/3)
14/04/15 Barbara Russo
public class BagOfObjects {
private ArrayList listOfObjects = new ArrayList(); private ArrayList listOfCounts = new ArrayList(); public void add(Object o) { boolean found = false; for ( int i = 0; i < listOfCounts.size(); i++ ) { ClassCount c = (ClassCount) listOfCounts.get(i); if ( c.getClassOfObjects().equals( o.getClass() ) ) { c.incrementOccurrences(); found = true; break; } } if (!found) { ClassCount c = new ClassCount( o.getClass(), 1 ); listOfCounts.add(c); } listOfObjects.add(o); } // continue …
indicates whether the two references point to the same object of type Class
14/04/15
22
Example in Java (2/3)
14/04/15 Barbara Russo
// continued class BagOfObjects…
public int getHowMany(Class aClass) { for ( int i = 0; i < listOfCounts.size(); i++ ) { ClassCount c = (ClassCount) listOfCounts.get( i ); if ( c.getClassOfObjects().equals( aClass ) ) { return c.getOccurrences(); } } return 0; // No objects found in listOfCounts! }
// continue …
Example in Java (3/3)
14/04/15 Barbara Russo
// continued class BagOfObjects…
public String toString() { String res = "In total there are: \n"; for (int i = 0; i < listOfCounts.size(); i++) { ClassCount c = (ClassCount) listOfCounts.get(i); res += "\t" + c.getOccurrences() + " elements of " +
c.getClassOfObjects() + "\n"; } res += "\n-----\n"; for ( int i = 0; i < listOfObjects.size(); i++ ) { Object t = listOfObjects.get(i); res += "\tElement " + i + " is " + t + " of " + t.getClass() + " \n "; } return res; }
} // end of class BagOfObjects