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 { staticThis 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:
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 { staticJava 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:
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.