This can be a difficult problem. I think Cruncher's solution, add
doStuff
to X and override it in A
, B
, C
, is the simplest and
best solution when it's appropriate. However, it isn't always
appropriate, because of the Single responsibility
principle.
(I think that's the correct term. My apologies if I get some
terminology wrong, I'm not entirely up-to-date on all of the terms.)
The idea is that you shouldn't necessarily doStuff
to X
if it has
nothing to do with the purpose of X
. If X
and Y
are part of the
same "team", i.e. they've been both set up to serve the purpose of one
particular application, then it's probably OK.
But suppose you have an abstract Shape
class that has subclasses
Circle
, Square
, Undecagon
, RandomBlob
, etc. There will be
some methods that belong in the Shape
class that would be useful to
any application that uses the Shape
class. But now say you are
writing a game that uses some of those shapes, and you want a
polymorphic operation that determines what happens when the shape gets
eaten by a flying monkey. You wouldn't want to add an abstract
computeEatenByFlyingMonkey
method to your Shape
class, even if the
class were your own creation and not in someone else's library,
because that would be just too specific for a class that could be
generally used for other purposes than this one game.
I can think of a couple ways to approach this.
If it's not appropriate (or not possible) to add doStuff
to X
, but
if A
, B
, and C
are more closely connected to your application so
that adding doStuff
to them is appropriate, you can add another
class:
public abstract class XWithStuff extends X {
// repeat any constructors in X, making them all be just
// calls to super(...)
public abstract void doStuff (Boolean flag);
}
public class A extends XWithStuff {
@Override
public void doStuff (Boolean flag) { ... }
}
and so on for every other class. (XWithStuff
is just an example
name; in real life, a name that contains both "X" and some reference
to the application or purpose is probably better.) (P.S. I don't know
why you're using Boolean
instead of boolean
but I'm leaving it
that way in case there's a good reason.)
If it's also not appropriate or not possible to add doStuff
to A
,
B
, and C
, here's a possible solution:
public interface StuffInterface {
public void doStuff (Boolean flag);
}
public class AWithStuff extends A implements StuffInterface {
@Override
public void doStuff (Boolean flag) { ... }
}
and then in your program create objects of class AWithStuff
instead
of A
, etc. To call doStuff
on an X
:
void doStuff (X x, Boolean flag) {
if (x instanceof StuffInterface) {
((StuffInterface) x).doStuff (flag);
} else {
throw new IllegalArgumentException ();
}
}
If that's not an option and you have to deal directly with A
, B
,
etc., and you can't add doStuff
to those classes, then any solution
will be a bit hacky. If you don't want to use if
-then
-else
, you
could look into the visitor pattern, or you could conceivably create
a HashMap<Class<?>,Interf>
that would map A.class
, B.class
,
etc., to some interface object that calls the correct doStuff
. But
I haven't worked out the details. (Actually, the "visitor pattern" probably wouldn't be appropriate unless you have some sort of complex structure composed of objects of type X.)