Java binds (most) method invocations dynamically with respect to the object on which they are invoked, but it determines type signatures statically. Thus, when your code ...
for (SuperClass o : objects)
get(o);
... is analyzed at compile time, Java must select a get()
method signature based only on the information it has at that time, which is that o
is a SuperClass
. Moreover, it will choose exactly one method signature, for an existing method on the relevant class. At run time, it will resolve that method signature against the class of the object on which it is invoked, considering other signatures only to the extent needed for supporting covariant return types.
That you have a requirement such as you do creates a distinct code smell. It makes sense to collect objects in a List<SuperClass>
only for purposes that are served equally well (and perhaps polymorphically) by any SuperClass
, which is not the case for you. If you cannot change that aspect of the design, then there are multiple ways you could proceed, including ...
An ugly hack way
Use instanceof
and casting:
for (SuperClass o : objects) {
if (o instanceof ChildClass1) {
get((ChildClass1) o);
} else if (o instanceof ChildClass2) {
get((ChildClass2) o);
} else if (o == null) {
// do something appropriate
} else {
throw new SomeKindOfException(
"Don't know what to do with a " + o.getClass());
}
}
Double dispactch / inversion of control
Give the superclass a method ...
abstract void acceptGetter(ClassThatGets objectThatGets);
... and have each subclass implement it the same way:
void acceptGetter(ClassThatGets objectThatGets) {
objectThatGets.get(this);
}
Then replace your problematic loop with ...
for (SuperClass o : objects)
o.acceptGetter(this);
... where it is assumed that that code appears in class ClassThatGets
, which declares the get()
methods you described, or in a subclass of that class.
There are surely other alternatives as well, some of which may fit more cleanly into your overall design.