This post originated from an RSS feed registered with Java Buzz
by Weiqi Gao.
Original Post: It Is Generic After All
Feed Title: Weiqi Gao's Weblog
Feed URL: http://www.weiqigao.com/blog/rss.xml
Feed Description: Sharing My Experience...
Bruce Eckel, October 8, 2004: ... Using adapters like this would seem to compensate for the lack of latent typing, and thus allow you to write genuinely generic code.
From it's announcement in June 1998, to the final induction into Java 5.0 in August 2004, Java generics has gone through over six years of maturation.
Originally hailed as an efficient and backward compatible extension to the JDK rooted in some esoteric theory, Java generics, especially its erasure based implementation has been under much criticism lately (including on this blog).
One of the complaints leveled against Java generics is that it is not generic:
public class CallF {
public <T> void callF(T t) {
t.f(); // can't call f() here
}
}
In his latest weblog, Bruce Eckel offers several ways around the problem by using adapters. One of them, termed surprising by Bruce, uses an abstract generic adapter instantiated and anonymously extended at the call site.
Here's a distilled version of the example:
[weiqi@gao] $ cat A.java
public class A {
public void f() {
System.out.println("A.f()");
}
}
[weiqi@gao] $ cat CallF.java
public class CallF {
public <T> void callF(HasF<T> adapter) {
adapter.f();
}
}
[weiqi@gao] $ cat HasF.java
public abstract class HasF<T> {
protected T t;
public HasF(T t) {
this.t = t;
}
public abstract void f();
}
[weiqi@gao] $ cat Main.java
public class Main {
public static void main(String[] args) {
A a = new A();
CallF f = new CallF();
f.callF(new HasF<A>(a) {
public void f() {
t.f(); // OK to call f() since t is of type A here
}
});
}
}
[weiqi@gao] $ javac *.java
[weiqi@gao] $ java Main
A.f()
Once understood, the code is quite clear.
The classes CallF and HasF is the library that just want to call a method named f(). No extra requirement is placed on T except that it has the f() method. This qualifies them as true generic algorithm classes.
The class A is a POJO that has an f() method.
In Main we manage to hook up the "library" and the POJO and have the "library", which knows nothing about the POJO, call a method on the POJO, which knows nothing about the "library", just like in C++. Great!
Looking back at the solution, I can't help but notice all the clutter. Suddenly the irony is apparent: We went all the way to add generics into Java, ostensibly to simplify our Java code, yet at the end our code is even more cluttered.
Could we have done the same thing without generics? Of course. This is the land of erasure. Decompiling the classes with JODE, I get:
[weiqi@gao] $ jode -d jode A CallF HasF Main
Jode (c) 1998-2001 Jochen Hoenicke <jochen@gnu.org>
A
CallF
HasF
Main
Decompiled 4 classes.
[weiqi@gao] $ cd jode/
[weiqi@gao] $ cat A.java CallF.java HasF.java Main.java
public class A
{
public void f() {
System.out.println("A.f()");
}
}
public class CallF
{
public void callF(HasF hasf) {
hasf.f();
}
}
public abstract class HasF
{
protected Object t;
public HasF(Object object) {
t = object;
}
public abstract void f();
}
public class Main
{
public static void main(String[] strings) {
A a = new A();
CallF callf = new CallF();
callf.callF(new HasF(a) {
public void f() {
((A) t).f();
}
});
}
}
[weiqi@gao] $ javac *.java
[weiqi@gao] $ java Main
A.f()
Comparing this with the generic classes, we discover that all of our generics mumbo-jumbo bought us exactly one puny little cast!.
Oh wait a minute, have I just written a genuinely generic algorithm in pre 5.0 Java without using generics?