Blub
Blub

Reputation: 3822

Java Method overloading and calling

Let's say I have a method with one argument and depending on the type of argument the method is supposed to do different things.

Let's call the method doMethod():

private int doMethod(Class1 x) {
    // do something with x
}

private int doMethod(Class2 x) {
   // do someting else with x
}

...

I have several of these methods. Now here comes my problem: I have a different method which takes an argument that is the superclass of all other classes from above (but in reality it is ALWAYS one of its subclasses):

private void otherMethod(SuperClass obj) {
      // I do something clever with obj, 
      // which is the super class of Class1, Class2, .. Classn
      // however this method is always called with either Class1, Class2, etc.
      // never with an instance of SuperClass itself.

      // finally I want to execute doMethod() on obj:
      int result = doMethod(obj);        

      // fails because doMethod(SuperClass) does not exist
}

So finally I end up with creating a doMethod(Superclass obj), check obj with instanceof and then call doMethod() with a cast.

There must be a better method to do this no? I just can't think of it at the moment, so maybe someone can help me out :)

Thanks a lot!

Upvotes: 3

Views: 161

Answers (1)

Paul
Paul

Reputation: 3058

A couple of possibilities:

Subclasses + Visitor Pattern

You could create a subclass for each of the subclasses, if not final. You could them provide a visit() method and implement your additional operation doMethod() with the Visitor pattern. This would open up the possibility of adding different operations later. The Visitor pattern, if done right, should make it impossible to forget to add the operation to a new subclass (as it won't compile). This may or may not be a good thing, depending on your needs.

The sub-subclasses are the part about this solution I don't like. There is a danger of exploding class hierarchies and complexity here: I'd make the extra subclasses you add final if at all possible. You'd also be in trouble is any of the classes you are extending become final in future (a backward-compatibility break, but not impossible).

Example here: http://www.javacodegeeks.com/2013/08/visitor-design-pattern-in-java-example-tutorial.html (in this example the developer has access to the base class and adds the visit() method there, whereas you would have to add it to a common interface implemented by your extra subclasses).

Aggregation + Visitor Pattern

A variant of the above would be to create a parallel class hierarchy where your own classes contain (rather than subclass) an instance of the classes from the other hierarchy. You add visit() to your own class hierarchy (and any other methods you want) and also provide a means to access the underlying types. You should be able to wrap this up nicely in generics.

Again, though, there is potentially a lot of complexity here.

Map of 'Handlers'

You could look up the operations as implementations of an interface in a map keyed by the instance's type.

// Java-y pseudo-code...
Map<Class<? extends SuperClass>,Function<V,R>> doMethods = new HashMap<>();
doMethods.add(FooClass.class, FooDoMethod.INSTANCE);
doMethods.add(BarClass.class, BarDoMethod.INSTANCE);
...
public R doMethod(V it) {
    Class<? extends V> cls = it.getClass();
    Function<? extends V,R> fn = doMethods.get(cls);
    if(fn==null)
        throw new UnsupportedOperationException(
            "Can't doMethod() on a "+cls.getName());
    return fn.apply(it);
}

The INSTANCE singleton objects here would implement something like the new Function interface in Java 1.8 (or Comparable before 1.8) - i.e. be implementations of an interface with a single method that implements the operation. You then extend the types for which the operation is supported by adding more entries to the map. You will need to add an explicit entry for each supported type (even intermediate base classes, if they can actually have instances - analogous to JComponent and JButton, in Swing (both of which can exist a real instances). You could, of course, add the same implementation for multiple types, if they are to implement it in the same way.

You could then hide dispatching to the mapped handlers (and the map itself) in some other class.

(P.S. Sorry, no javac at hand, otherwise I'd have tried these out properly.)

Upvotes: 2

Related Questions