Question

Originally I had a design problem where I needed five subclasses of a superclass, where all but two would use the same generic method of doing things and the other two classes would need special handling. I wanted to avoid writing the method five times; two special cases and three identical ones.

So I had every class inherit SuperClass, and its doSomething() method, and had SubClassSpecial1 and SubClassSpecial2 override with their own doSomeThing method.

This was all fine until I wrote a method which looked something like

void fooBar(SuperClass obj) {
    obj.doSomething()
}

and it could be called as fooBar( new SubClassSpecial1() );

The problem is that the runtime class of the obj variable is now that of its superclass, and thus it will call the methods defined in the superclass. I could, of course, make an abstract doSometing() method in the superclass, and make each subclass implement its own version, but that would duplicate code in three of the classes. And I want to avoid doing that ...

I would lose any gain polymorphism gives if I had a lot of branching through

if(obj.getClass().getName() == "SubClassSpecial1" )  ((SubClassSpecial1) obj).doSomething()K;
else if ...

So what should I do in order to make the design more elegant and un-hackish?

Was it helpful?

Solution

What you've described should work fine as is.

Is obj.doSomething() actually calling the superclass' implementation? If it is, you're not overriding it properly. Check that you haven't changed the signature in your overridden versions.

OTHER TIPS

I'm a bit confused by this part of your question:

Of course, what I wanted to do was have each object call its own version of doSomething(), but failed to realise that in order to do that, obj needed to be declared as one of the subclassed methods. And now it's a mess.

Ofcourse the declaration doesn't matter, the doSomething() method will always be invoked according to the runtime type of the class.

So I think what you were trying to do should work just fine, e.g. these declarations can all be used to pass to the foobar method:

SuperClass sc1 = new SubClassSpecial1();
SubClassSpecial2 sc2 = new SubClassSpecial2();
//etc..

When you have this:

void fooBar(SuperClass obj) {
    obj.doSomething();
}

then the compile-time type of obj is SuperClass. This means that the compiler is going to check that SuperClass has a doSomething() method.
At runtime you can substitute a subclass of SuperClass, this is the Liskov Substitution Principle. Method foobar() does not know, and should not know, what the runtime type of obj is, only that it derives from SuperClass and so doSomething() can be called.

As to your example:

fooBar( new SubClassSpecial1() );

In this case you happen to know that the runtime type of the parameter is SubClassSpecial1 which specifically overrides doSomething(). In all cases the right method is called.

A word about refactoring. You may want to consider refactoring your hierarchy.
Your base class SuperClass should define doSomething() as abstract. Your three classes that need the same implementation of doSomething() should inherit it from an intermediate base class which has that specific implementation. Your two special classes should inherit directly from SuperClass and have their own implementation of doSomething().

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top