Running on Java 22-ea+27-2262 (Preview)
Home of The JavaSpecialists' Newsletter

034Generic Types with Dynamic Decorators

Author: Dr. Heinz M. KabutzDate: 2001-10-24Java Version: 1.3Category: Language
 

Abstract: Prior to Java 5, we had to jump through all sorts of hoops to get something like generics. In this newsletter we use "dynamic decorators" to produce type-safe iterators dynamically.

 

Welcome to the 34th issue of The Java(tm) Specialists' Newsletter, in which we look at how we can apply the dynamic decorators to produce type-safe iterators dynamically. I know that some of you will be tempted to write to me how "that will all be in JDK 5" or "here are 1001 reasons why your construct is not useful". The purpose of this newsletter is to make you think, please don't forget that. It is not to provide you with cut & dry solutions to your problems.

Thanks to Dr. Christoph Jung and Bruce Eckel for the ideas that spawned this newsletter. Some of these examples will probably be featured in the "Thinking in Patterns" book that Bruce is busy writing at the moment. I can't wait for that to get published ...

1486 members are currently subscribed from 55 countries

javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.

Generic Types with Dynamic Decorators

Proxy vs. Decorator

What is the difference between a Proxy and a Decorator?

If we look at the structure of these patterns in the GoF book, we see that the Proxy has an association from the Proxy class to the RealSubject class (actually, it would be more versatile to have an association between Proxy and the Subject interface). Essentially the structure in Java code would look like this:

interface Subject {
  public void request();
}

class RealSubject implements Subject {
  public void request() { /* do something */ }
}

class Proxy implements Subject {
  private Subject realSubject;
  Proxy(Subject realSubject) {
    this.realSubject = realSubject;
  }
  public void request() {
    /* do something, then */
    realSubject.request();
  }
}

The Decorator pattern's structure has an aggregation from the Decorator class to the Component. The structure in Java code would look like this:

interface Component {
  public void operation();
}

class ConcreteComponent implements Component {
  public void operation() {
    /* do something */
  }
}

class Decorator implements Subject {
  private Component component;
  Decorator(Component component) {
    this.component = component;
  }
  public void operation() {
    /* do something, then */
    component.operation();
  }
}

class ConcreteDecorator extends Decorator {
  ConcreteDecorator(Component component) {
    super(component);
  }
  public void anotherOperation() {
    /* decorate the other operation in some way, then call the
     other operation() */
    operation();
  }
}

If we now change the names of the classes to A, B, C and the method to f(), Proxy becomes:

interface A {
  public void f();
}

class B implements A {
  public void f() { /* do something */ }
}

class C implements A {
  private A a;
  C(A a) {
    this.a = a;
  }
  public void f() {
    /* do something, then */
    a.f();
  }
}

And if we do the same to Decorator, that becomes:

interface A {
  public void f();
}

class B implements A{
  public void f() { /* do something */ }
}

class C implements A {
  private A a;
  C(A a) {
    this.a = a;
  }
  public void f() {
    /* do something, then */
    a.f();
  }
}

class D extends C {
  D(A a) {
    super(a);
  }
  public void g() {
    /* decorate the other operation in some way, then call the
     other operation() */
    f();
  }
}

Oops, by looking at the structure alone, we see that there is not that much difference between Proxy and Decorator. We also have to look at the intents:

Proxy: Provide a surrogate or placeholder for another object to control access to it.

Decorator: Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

We have to look at the structure and the intent and the applicability together if we want to figure out which pattern we are looking at. Even then, since a lot of the patterns are very similar, we can get into heated debates about this. Bruce Eckel and I exchanged some interesting emails trying to decide whether what you are about to see is the Decorator, Adapter or Proxy.

If you got lost in this discussion, you are missing out on one of the most powerful techniques developed in Software Engineering in the last decade, that being Design Patterns. I've developed a new classroom-based course that will teach you how to recognise and apply Object-Oriented Design Patterns. Please look at our website for more information.

The answer to the question of "what is the difference between a Proxy and a Decorator" lies mainly in the intent. We intend to use a Decorator to extend the interface of a object dynamically, whereas a Proxy is intended to provide a surrogate object for the real object.

Enough of a Design Patterns lesson. Let's get to the meat of today's newsletter.

Generic Types with Dynamic Decorators

Bruce Eckel sent me an email asking if I had some nice examples for using the Dynamic Proxies of JDK 1.3. I passed the question on to my friend Christoph Jung who sent an interesting reply. He said that they use dynamic proxies quite a lot in order to handle type-safe collections. The issues addressed actually reminded me of generic collections, due in JDK 5 hopefully.

Let's remember how we used collections in the past. Say we had a stray animal pound in which we had collections of dogs and cats. This code is what the "client" code of our dogs and cats would use, i.e. in the client code we have to keep on doing type casting down to the correct type while iterating through the collection.

public class Cat {
  private final String species;
  public Cat(String species) { this.species = species; }
  public String toString() { return species; }
  public void meow() { System.out.println(this + ": Meow"); }
}

public class Dog {
  private final String species;
  public Dog(String species) { this.species = species; }
  public String toString() { return species; }
  public void bark() { System.out.println(this + ": Woof"); }
}

import java.util.*;
public class Pound {
  private Collection dogs;
  private Collection cats;
  public Pound(Dog[] dogs, Cat[] cats) {
    this.dogs = Arrays.asList(dogs);
    this.cats = Arrays.asList(cats);
  }
  public void makeNoise() {
    Iterator dog_it = dogs.iterator();
    while(dog_it.hasNext()) {
      ((Dog)dog_it.next()).bark(); // we have to downcast!
    }
    Iterator cat_it = cats.iterator();
    while(cat_it.hasNext()) {
      ((Cat)cat_it.next()).meow(); // we have to downcast!
    }
  }
}

Wouldn't it be nice if we could have a collection for dogs and a collection for cats so that we would not have to worry about downcasting? Indeed, but Java does not support generics yet, I hear some of you say.

How could we improve our situation by abusing dynamic proxies? (For a detailed newsletter on dynamic proxies, please refer to Issue #5) What if we decided on a naming convention within our objects that we want to collect? Let's say we change Dog to be the following:

public class Dog {
  private final String species;
  public Dog(String species) { this.species = species; }
  public String toString() { return species; }
  public void bark() { System.out.println(this + ": Woof"); }
  public static interface Collection extends java.util.Collection {
    Iterator dogIterator();
  }
  public static interface Iterator extends java.util.Iterator {
    Dog nextDog();
  }
}
}

Our client code could therefore use Dog.Collection and call method dogIterator(), which would return a Dog.Iterator with the specialised method nextDog(). This means we never have to downcast the anonymous objects to Dog!

So, how do we actually make an instance of this Dog.Collection? Simple, dynamic proxies ... with a twist. First of all, I write a GenericFactory class (the Fowler Factory Method pattern, not the GoF). This GenericFactory has a makeCollection() method where I pass in an instance of the collection backing the Dog.Collection (this is where the adapter pattern comes in), and the type that I want to support. It is assumed that the type you pass in (e.g. Dog.class) has an inner interface called Collection with a method dogIterator() and another inner interface called Iterator that contains a method nextDog(). If these are not found we throw an IllegalArgumentException.

import java.lang.reflect.Proxy;
import java.util.*;
public class GenericFactory {
  public static Collection makeCollection(Collection backing,
      Class type) {
    GenericCollection gen = new GenericCollection(backing, type);
    return (Collection)Proxy.newProxyInstance(
      gen.getTypeCollectionClass().getClassLoader(),
      new Class[] { gen.getTypeCollectionClass() },
      gen);
  }
  /* please ignore makeIterator for now ... */
  public static Iterator makeIterator(Iterator backing, Class type) {
    GenericIterator gen = new GenericIterator(backing, type);
    return (Iterator)Proxy.newProxyInstance(
      gen.getTypeIteratorClass().getClassLoader(),
      new Class[] { gen.getTypeIteratorClass() },
      gen);
  }
}

The GenericCollection class looks something like this. What it does is intercept any calls to the Dog.Collection proxy and checks if it is the dogIterator() method. If it is, it uses the GenericFactory to make a Dog.Iterator.

import java.lang.reflect.*;
import java.util.*;
public class GenericCollection implements InvocationHandler {
  private final Collection backing;
  private final Class type;
  private final Class typeCollection;

  public GenericCollection(Collection backing, Class type) {
    this.backing = backing;
    this.type = type;
    typeCollection = discoverCollection();
  }

  /** Find the correct inner class Collection interface.
    @throws IllegalArgumentException if inner interface Collection
    not found */
  private Class discoverCollection() {
    Class[] innerClasses = type.getClasses();
    for (int i=0; i<innerClasses.length; i++) {
      if (innerClasses[i].getName().equals(
          type.getName() + "$Collection")) {
        return innerClasses[i];
      }
    }
    throw new IllegalArgumentException(
      "Class does not contain inner Collection interface");
  }

  public Class getTypeCollectionClass() {
    return typeCollection;
  }

  /** This is the meat of the GenericCollection.  It finds any
    method with type name followed by iterator and hijacks it. */
  public Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable {
    if (method.getName().equalsIgnoreCase(type.getName() + "Iterator")) {
      return GenericFactory.makeIterator(backing.iterator(), type);
    }
    return method.invoke(backing, args);
  }
}

In order to make the picture complete, we also have to see the code that makes the Iterators. The GenericIterator looks very similar to the GenericCollection. I am sure we could have a GenericType from which both of those classes could inherit and which could contain common functionality. However, I was writing the code while trying to pacify a crying baby. Not that easy, seeing that I cleverly changed all the keys around on my notebook so that now the only way to use it is to touchtype. Almost dropped it a few times - the notebook, that is ;-)

import java.lang.reflect.*;
import java.util.*;

public class GenericIterator implements InvocationHandler {
  private final Iterator backing;
  private final Class type;
  private final Class typeIterator;

  public GenericIterator(Iterator backing, Class type) {
    this.backing = backing;
    this.type = type;
    typeIterator = discoverIterator();
  }

  /** Find the correct inner class Iterator interface.
    @throws IllegalArgumentException if inner interface Iterator
    not found */
  private Class discoverIterator() {
    Class[] innerClasses = type.getClasses();
    for (int i=0; i<innerClasses.length; i++) {
      if (innerClasses[i].getName().equals(
          type.getName() + "$Iterator")) {
        return innerClasses[i];
      }
    }
    throw new IllegalArgumentException(
      "Class does not contain inner Iterator interface");
  }

  public Class getTypeIteratorClass() {
    return typeIterator;
  }

  public Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable {
    if (method.getName().equalsIgnoreCase("next" + type.getName())) {
      return backing.next();
    }
    return method.invoke(backing, args);
  }
}

How do you use this code? Let's have another look at the newly improved Pound class. You will notice that I am making the collections in the constructor of the Pound, and after that I use those collections directly. Normally, the collections would contain Value Objects from your database, and they would be made dynamically by your framework.

import java.util.*;
public class Pound {
  private Dog.Collection dogs;
  private Cat.Collection cats;
  public Pound(Dog[] dogs, Cat[] cats) {
    this.dogs = (Dog.Collection)GenericFactory.makeCollection(
      new LinkedList(Arrays.asList(dogs)), Dog.class);
    this.cats = (Cat.Collection)GenericFactory.makeCollection(
      new ArrayList(Arrays.asList(cats)), Cat.class);
  }
  public void makeNoise() {
    Dog.Iterator dog_it = dogs.dogIterator();
    while(dog_it.hasNext()) {
      /* no more downcasting! */
      dog_it.nextDog().bark();
    }
    Cat.Iterator cat_it = cats.catIterator();
    while(cat_it.hasNext()) {
      /* no more downcasting! */
      cat_it.nextCat().meow();
    }
  }
}
}

We can test that in our PoundTest class by making a new Pound and calling the makeNoise method.

public class PoundTest {
  public static void main(String[] clargs) throws ClassNotFoundException {
    Pound spca = new Pound(
      new Dog[] {
        new Dog("Alsation"),
        new Dog("Bulldog"),
        new Dog("PavementSpecial") },
      new Cat[] {
        new Cat("RussianBlue"),
        new Cat("DeadBounce") });
    spca.makeNoise();
  }
}

With output of:

Alsation: Woof
Bulldog: Woof
PavementSpecial: Woof
RussianBlue: Meow
DeadBounce: Meow

Oh, by the way, Cat would look like this:

public class Cat {
  private final String species;
  public Cat(String species) { this.species = species; }
  public String toString() { return species; }
  public void meow() { System.out.println(this + ": Meow"); }
  public static interface Collection extends java.util.Collection {
    Iterator catIterator();
  }
  public static interface Iterator extends java.util.Iterator {
    Cat nextCat();
  }
}

Yes, I know this is only a small step towards all the things you can do with generics in C++, and hopefully in JDK 5. However, it has made me think in a different way about what we can do with dynamic "proxies".

Was That a Proxy or a Decorator?

Back to our earlier discussion of whether this was a Proxy or a Decorator. I say this is more of a Decorator than anything else, as we are dynamically and transparently extending the functionality of existing classes without subtyping. The interesting part is that the Decorator class is not actually a class per se, it is a naming convention.

Disclaimer

I know that the dynamic decorators shown in this newsletters have some deficiencies at the moment. For example, it is possible to add a Cat to a collection of Dogs without an exception being thrown immediately. In a production-quality system you should obviously check that. throw new ExerciseForTheReaderException();

Last Words

Yes, I know that I am harping on about Design Patterns. However, without a solid understanding of Design Patterns you will never be a real Java Specialist. So, don't delay, pop me an email today ... :-)

 

Comments

We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)

When you load these comments, you'll be connected to Disqus. Privacy Statement.

Related Articles

Browse the Newsletter Archive

About the Author

Heinz Kabutz Java Conference Speaker

Java Champion, author of the Javaspecialists Newsletter, conference speaking regular... About Heinz

Superpack '23

Superpack '23 Our entire Java Specialists Training in one huge bundle more...

Free Java Book

Dynamic Proxies in Java Book
Java Training

We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.

Java Consulting

We can help make your Java application run faster and trouble-shoot concurrency and performance bugs...