Multi-typed Parameters
In some cases you want to define a method parameter which is of a certain type (i.e a class or an interface), but also implements another interface.
Start Situation
If the first type is a class, the basic situation looks like (1):
class A
{
String getMyA();
}
interface B
{
String getMyB();
}
Now you have classes which do combine both of the above (2):
class C
extends A
implements B
{
}
class D
extends A
implements B
{
}
Finally you want to implement the following (3):
class X
{
static String getAB(/*C or D*/ object) {
return object.getMyA() + object.getMyB();
}
}
and use it like (4);
// somewhere else
C foo = new C();
System.out.println(X.getAB(foo));
D bar = new D();
System.out.println(X.getAB(bar));
Problem is how to implement the C or D
type in the getAB()
method.
Indeed C or D is misleading and too special.
What you really want is a type which is both A
and B
.
Good Solution
This requires at least Java 5. Here you can use a generic type parameter to your help. You only have to change (3) to look like:
class X
{
static <T extends A & B> String getAB(T object) {
return object.getMyA() + object.getMyB();
}
}
This is elegant and requires just a simple change at the place where it ought to be.
Of course this works for non-static methods the same, also for generic classes, and easily extends to more than 2 types. So it’s one of the patterns you should keep in your mental toolbox.
Special Cases
In time-critical code you should keep in mind that each access to object
’s
methods in getAB()
internally uses a cast. What the above code really looks like
is similar to:
class X
{
static String getAB(Object object) { // Java view of code, not your implementation
return ((A)object).getMyA() + ((B)object).getMyB(); // Java view of code, not your implementation
}
}
Casts are (nowadays moderately) expensive, so if you access the object several times in time-critical code you should do the unpacking ourself once:
class X
{
static <T extends A & B> String getAB(T object) {
A a = object; // here the cast is happening once
B b = object; // here the other cast is happening
// --- from here only access a and b ---
return a.getMyA() + b.getMyB();
}
}
Java optimization might be able to figure this out, too, but I’d prefer to be sure.
Bad Solutions
Before Java 5 you had only two options: insert a common base class or extend
the method getAB()
to accept two parameters.
But if you are using a modern Java implementation: Don’t do this!
Insert a Common Base Class (deprecated)
(1) and (4) above stay the same, but (2) now looks like
class Base
extends A
implements B
{
}
class C extends Base {}
class D extends Base {}
and (3) becomes
class X
{
static String getAB(Base object) {
return object.getMyA() + object.getMyB();
}
}
In some cases this might even clarify your class structure, but in other cases this would insert an otherwise useless class or interface making things unnecessarily complex.
Inserting a class can even be impossible. This happens for code from libraries, and also for a very special case I encountered:
enum C implements B { /* ... */}
enum D implements B { /* ... */}
Both C
and D
extend Enum
, and I wanted to write a method which
only accepts enums implementing the special interface B
. Of course
you now know how I did it:
public <E extends Enum<E> & B> void foo(E e) { // ...
Make the Method Take Two Parameters (deprecated)
(1) and (2) stay the same, (3) becomes
class X
{
static String getAB(A a, B b) {
return a.getMyA() + b.getMyB();
}
}
and in (4) you’ll have to duplicate the parameter
// somewhere else
C foo = new C();
System.out.println(X.getAB(foo, foo));
D bar = new D();
System.out.println(X.getAB(bar, bar));
This is obviously awkward, but removes the need to change the class structure in cases where this is impossible or not preferable.