Java Generics – Is Using Generics to Indicate Type Awareness a Good Idea?

genericsjava

I'm going to use Java for code/idea demonstration.

Imagine it's needed to define two interfaces: Observer and Observable (observer pattern). It's a good idea to have two generic interfaces, so there is no restriction on what is being observed, who is observing it (is even using generics about this?).

Java has a concrete (non-final) Observable class and an Observer interface. You have to delegate handling observers and notifying them on update to the concrete Observer class. This is one way of implementing the pattern. I don't know if it's the best approach (probably is), but for the sake of this example let's assume every class/type who wants to be observable, must implement the Observable interface below, and handle everything itself.

We should not forget the original Observer and Observable interface/object were implemented in JDK1.0, when generics were not available.

Now the question: Is this a good idea to merely declare a generic type in an interface to denote that the implementing class needs to be aware of some specific type? Or it is totally wrong way of using generics?

See:

public interface Observable<T> {
    // Only observers who are aware of type T are allowed.
    // But should really Observable be concerned about it?
    public void registerObserver(Observer<? extends T> o);
} 

// The T here denotes that implementing class will be aware of a type called T,
// (not that it can operate on a range of types).
public interface Observer<T> {
    // Observer is not aware of Observable interface directly. But it knows about T.
    public void update(T arg);
}

VS

public interface Observable {
    // Is it an observer? then it is allowed. That's all we need to know.
    public void registerObserver(Observer o);

    // Observers will be passed the instance of observable, 
    // then they call this method to fetch arg.
    // something like:
    // for(Observer o: this.observers) o.update(this);
    public <T> T getArg();
} 

public interface Observer {
    // Pass me an Observable who returns T and i'll do the rest!
    public void update(Observable o);
}

I feel I'm getting generics totally wrong.

P.S: Comparing this way of implementing the observer pattern (asking every type to re-implement these interfaces) vs the original java solution (delegation) we realize it's not really a good idea. It causes a lot of code duplicate. But it helps to understand generics better.

Update The actual problem with java.util.Observer is notifyObservers(Object arg)[link to doc] where arg is cast at runtime by observers. How can we avoid such cast?

Best Answer

...the narrow answer to your question is, when you upcast an object into Object, then necessarily you have to downcast it to the specific type later -- unless you're going to use the Object methods like toString, or else do an end-run around the compiler's type checking with runtime introspection (e.g. Reflection).

When you upcast an object (e.g. something.setAsObject("a string")), you lose type information about that object. You can get it back in various ways, but generics is how to carry the type on for the compiler to know what to expect later, and doesn't require the downcast back to what it was before.

...

Java generics open the door for parametric polymorphism, which is a different type of polymorphism than we usually talk about in OO. It allows for creating "container" or "wrapper" types that add behaviors independent of underlying types. As wikipedia puts it, " a function or a data type can be written generically so that it can handle values identically without depending on their type"

For example, for a List<T>, what the <T> says is that a list works the same regardless of whether it's a List<String> or a List<Integer>. You can perform the same operations, and when you're done you can retrieve objects of whatever type the list contains.

So, in a funny way, generics allow a type to be unaware of the underlying type. A List<T> doesn't know, doesn't care, what type it contains. An Observer<T> doesn't know, doesn't care, what type it's observing. It just provides certain semantics on top of that type.

This allows the code to avoid a downcast. Downcasts are viewed as bad in OO, because they raises the possibility of a runtime class cast exception -- you lose the benefit of type-checking by the compiler.

In your example, making your Observer interfaces generic is saying, "I don't care what type I'm observing, I'm going to provide this Observer mechanism on top of it." Seems like an apt application of generics.

(For what it's worth, when you get into the business of combining parametric polymorphism (i.e. container types that add behaviors) with chained or fluent interfaces, you can create some really powerful and elegant programmatic APIs.)