interface CanQuack
{
void quack();
}
interface CanWalk
{
void walk();
}
<T extends CanQuack & CanWalk> void doDucklikeThings(T t)
{
t.quack();
t.walk();
}
You can pass anything to this method that implements CanWalk and CanQuack.
Of course, this is optimally flexible when you have one method per interface, but that's not a problem.
I can easily see the dependencies of doDucklikeThings, whereas if I had a larger Duck interface, e.g.:
interface Duck
{
void quack();
void walk();
void eatAFish();
}
I wouldn't be able to tell by looking at the signature of a method that used Duck, whether it would call eatAFish.
This works well for reducing coupling, and would probably be better than
this Proxy-based attempt.
There is no runtime cost, thanks to type erasure. It's rare that someone thanks Sun for type erasure, it seems, but I have got to grips with generics fairly well, thanks to Angelika Langer's
excellent generics FAQ, and I find it very useful.
I have applied this technique to a lot of the 20kloc program that I work on,
IPSim (a network simulator), but I am still in this process.
I hope you find this useful, or give me some damn good reasons why it isn't!