Table of Contents

A. Abstract reflection
A.1. Ways how to deal with object

List of figures

A.1. Ways how to deal with object

List of examples

A.1. Direct manipulation
A.2. Reflection
A.3. Introspection
A.4. Abstract reflection

A. Abstract reflection

abstract

Abstract reflection package is a superstructure of reflection and a more flexible analogy to introspection known from JavaBeans specification. It examines the class in order to find all the traits (more general then properties) and methods in accordance with highly customizable patterns. As a result of the examination it creates appropriate instance of abstract class - AClass. Abstract class encapsulates means of universal access to class properties and methods.

A.1. Ways how to deal with object

Abstract reflection is rather a library or framework then a standalone application, so the reader should be at least a bit comfortable with programming, especially Java programming, to be able to fully understand it. In the following paragraphs I will discuss the differencies between abstract reflection and alternatives (reflection, introspection..) in order to show, that there is a real necessity of abstract reflection. Speaking ahead of time, it's able to improve the concrete applications built on it - their flexibility, stability, maintainability; shortens development time, reduces costs.. Following demonstrative examples will be, naturally, in Java.

Let's have a class HumanBeing such as:

public class HumanBeing
{
    public int age = 0;
   
    public String getName() {/* some code */}
    public void setName(String name) {/* some code */}
    
    public Vector obtainFriends() {/* some code */}        
    public void remFriend(String friend) {/* some code */}
    public void addFriend(String friend) {/* some code */}       
}

Suppose that we need to instantiate John, put his friends down and then print everything out. The most natural solution would be such that:

Example A.1. Direct manipulation

//instantiate John
HumanBeing john = new HumanBeing();
//set the name
john.setName("John");
//put the friends down
john.addFriend("Tom");
john.addFriend("Alice");
//read name
String name = John.getName();
//read friends
Vector friends = john.obtainFriends();
//print out John and friends
System.out.println(name+" has friends:");
Iterator iter = friends.iterator();
while (iter.hasNext())
    System.out.println(iter.next());

It seems easy. Why to complicate it? Is there a real necessity of more layers of access? I guess you know, what's the main pitfall of direct manipulation - it requires the interface of manipulated class (HumanBeing) to be known by the programmer before any code can be written. Moreover, the compiler itself have to know the class before compliation. Is there any way how to manipulate the object unkonwn in advance?

You know, the most aged Java solution is called reflection, tersely said "ability to introduce itself" - supply in a runtime a list of methods and data fields for invocation. Let's have a look, how is it possible to use the reflection to achieve the same results, as in previous example:

Example A.2. Reflection

//instantiate John
String className = "xermes.areflect.example.HumanBeing";
Class humanClass = HumanBeing.class.forName(className);
Object john = humanClass.newInstance();
//set the name
Method setName = humanClass.getMethod("setName",new Class[] {String.class});
setName.invoke(john, new Object[] {"John"});
//put the friends down
Method addFriend = humanClass.getMethod("addFriend",new Class[] {String.class});
addFriend.invoke(john, new Object[] {"Tom"});
addFriend.invoke(john, new Object[] {"Alice"});
//read name
Method getName = humanClass.getMethod("getName",new Class[] {});
String name = (String)getName.invoke(john, new Object[] {});
//read friends
Method obtainFriends = humanClass.getMethod("obtainFriends",new Class[] {});
Vector friends = (Vector)obtainFriends.invoke(john, new Object[] {});
//print out John and friends
System.out.println(name+" has friends:");
Iterator iter = friends.iterator();
while (iter.hasNext())
    System.out.println(iter.next()); 
  

As you can see (and as you probably allready know), by reflection we can only call (invoke) methods. It's not a bad inherently - reflection works well as a low-level way how to deal with unknown objects. Nothing less, but nothing more and it may be inconvenient sometimes. The main reason is, that the reflection doesn't understand the terms of OOP, such as "proprerty". We need a higher level of abstraction when dealing with unknown objects. It's not necessary to achieve functional targets, but it would allow write the functionally equivalent program in a more natural flavor, more object-oriented and also the program might be shorter. And, you know, if the code becomes neat, short and readable, the number of programmer's bugs descends and maintainability in general rises. I guess, it was a Sun's motivation, why the work on the introspection has been started. With introspection, it's possible to handle with JavaBeans conforming objects. According example follows:

Example A.3. Introspection

//instantiate John
String className = "xermes.areflect.example.HumanBeing";
Class humanClass = HumanBeing.class.forName(className);
Object john = humanClass.newInstance();
//set the name
BeanInfo humanClassInfo = Introspector.getBeanInfo(humanClass);
PropertyDescriptor[] properties = humanClassInfo.getPropertyDescriptors();
PropertyDescriptor nameProp = null;
for (int i = 0; i < properties.length; i++)
{
    if (properties[i].getName().equals("name"))
    {
        nameProp = properties[i];
        break;
    }
}
nameProp.getWriteMethod().invoke(john, new Object[] {"John"});
//put the friends down
//it's impossible to do it directly by means of Introspection
//read name
String name = (String)nameProp.getReadMethod().invoke(john, new Object[] {});
//read friends
//it's impossible to do it directly by means of Introspection
//print out John and friends
System.out.println(name+" maybe has some friends.");
  

Someone could mention absence of method like getPropertyDescriptor(String name), that leads to walking through all the properties avilable; I think, this is only a minor detail and therefore can be ignored, becouse if someone needs such method, a simple decorator, several lines of code long, would be able to solve that. But there is a more painful problem: Introspection is tightly coupled with JavaBeans specification. It has been designed so. Intorspection is inherently unable to understand non-JavaBean properties, such as friends from our example. But there are common situations, where JavaBeans don't suite well. For instance think of multiple traits, mentioned yet. The only pattern allowed by JavaBeans, and therefore supported by introspection, is:

public Bah[] getFoo();
public void setFoo(Bah a[]);
public Bah getFoo(int a);
public void setFoo(int a, Bah b);

Standard Java arrays are a bit rigid in comparison with popular collections . It may be uncomfortable, but sometimes even unacceptable. Is there any way how to manipulate objects on similar abstract base, as with introspection, but without inconvenient restrictions? There was no such way, so the abstract reflection package has been designed, implemented and tested. It's purpose is exactly that - be a more flexible alternative to introspection. Let's go back to our example:

Example A.4. Abstract reflection

//instantiate John
String className = "xermes.areflect.example.HumanBeing";
Class humanClass = HumanBeing.class.forName(className);
Object john = humanClass.newInstance();
//set the name
AClass humanAClass = AFactory.getAClass(humanClass);
TraitSimple nameTrait = (TraitSimple)humanAClass.getTrait("name");
nameTrait.set(john, "John");
//put the friends down
TraitMultiple friendsTrait = (TraitMultiple)humanAClass.getTrait("friend");
friendsTrait.add(john, "Tom");
friendsTrait.add(john, "Alice");
//read name
String name = (String)nameTrait.get(john);
//read friends
Vector friends = (Vector)friendsTrait.obtain(john);
//print out John and friends
System.out.println(name+" has friends:");
Iterator iter = friends.iterator();
while (iter.hasNext())
    System.out.println(iter.next());

Abstract reflection thinks of the object as of the set of recognized traits. What does the "trait" mean? One example may be the pair of "accessors" alias property, as defined in JavaBeans specification (getFoo, setFoo). But imagine the public data field (public String foo) - it is principially equivalent to the property, so it should be treated exactly the same way - and the abtract reflection does so. Speaking about multiple properties, some of the most common examples could be common arrays (public String[] foo) and standard Java collections (Vector foo). We should also think about user-specific sets of methods (addFoo, remFoo, obtainFoos). If I try to condense it, almost anythig may act as a trait. How do we accomplished so high level of flexibility in abstract reflection? Abstract class factory delegates the task of recognizing traits to the special interface called Finder. Abstract reflection user can pick one or more from the common (supplied) " finders or implement own, completely specific. Therefore, crucially, program based on the abstract reflection doesn't have to care, how are the traits of object both internally implemented and externally published by object interface, if the finder recognized them successfully.

Ways how to deal with object

Figure A.1. Ways how to deal with object

Abstract reflection itself has grown to midsized framework (about 1000 hours of analysis, development, testing; the source code is about 250 kB). It would fit anywhere, the traits of unknown objects should be processed. We used it e.g. as a basis for the user interface generator (provides ui for given objects). The generator naturally exploits the strenghts of abstract reflection, so gives no radical restrictions on supported objects. But it's only one example from the great variety of possible utilizations of abstract reflection.

Glossary