0

I have an abstract superclass

public abstact class SuperClass

2 classes extend this superclass

public class ChildClass1 extends SuperClass
public class ChildClass2 extends SuperClass

I have an arraylist

List<SuperClass> objects = new ArrayList<SuperClass>();

I have a method that has 2 different parameter list

public void get(ChildClass1 o)
public void get(ChildClass2 o)

When trying something like this:

for (SuperClass o : objects)
    get(o);

I got an error: "The method get() is not applicable for the arguments SuperClass"

How should I implement this in a way that every element in the list could call the right method? Is the only way I can implement this is with instanceof and casting it or is there a nicer way?

2 Answers2

0

This is not possible as Java decides at compile time which one of these methods to use. You could have a get method defined in SuperClass, then the code would look like this:

for (SuperClass o : objects)
  o.get();

A quick Stackoverflow search lead me to this thread there is more information for you: overloading and compile time binding. Someone proposed to use the visitor pattern there. That might be an alternative, too. As every Java developer encounters the issue you have asked sooner or later I am sure there are plenty of interesting threads on this topic to be found here on Stackoverflow.

Community
  • 1
  • 1
mio
  • 562
  • 5
  • 19
0

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.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157