1

I have an abstract class inherited by two concrete classes.

public abstract class AbstractClass { 
    public abstract void operation1();
}

public class ConcreteClassA extends AbstractClass {
    @Override
    public void operation1() {
        // Do work
    }

    public void operation2() {
        // Do some other work
    }
}

public class ConcreteClassB extends AbstractClass {
    @Override
    public void operation1() {
        // Do work
    }
}

Now, to take advantage of dynamic binding I create two objects while programming to the interface.

private AbstractClass classA = new ConcreteClassA();   
private AbstractClass classB = new ConcreteClassB(); 

But this does not allow me to call method operation2() on classA. I can fix this by using a downcast.

((ConcreteClassA) classA).operation2();

But downcasts are considered ugly in OOP especially when you have to use them a lot. Alternatively, I can give up programming to the interface.

private ConcreteClassA classA = new ConcreteClassA();  

But then I lose the dynamic binding. Another option is to move operation2() to the AbstractClass so that I can restore the dynamic binding.

public abstract class AbstractClass { 
    public abstract void operation1();
    public abstract void operation2();
}

But then ConcreteClassB needs to override operation2() leaving the implementation empty since this class does not need this method.

Lastly, I could move operation2() to the AbstractClass and provide a default implementation which may be overridden or not.

public abstract class AbstractClass { 
    public abstract void operation1();

    public void operation2() {
        // Some default implementation
    }
}

But this gives classB access to operation2() which I would rather avoid.

There does not seem to be a clean solution to call subclass specific methods while maintaining dynamic binding at the same time. Or is there?

badjr
  • 2,166
  • 3
  • 20
  • 31
braaks55
  • 105
  • 1
  • 6

2 Answers2

1

There are at least a few ways to deal with this circumstance and, really, the right one depends on your particular requirements.

Ask yourself, "are both operation1 and operation2 part of the contract specified by my type?"

  • If the answer is clearly no, then you should not pollute the contract of your type by adding collateral methods to it. You should next ask yourself, "why am I not using interfaces to specify separate types, eg.: instead of AbstractClass, why am I not using MyInterface1 and MyInterface2 (each with its own separate contract)? Interfaces provide a limited form of multiple inheritance, and your implementing classes can implement any and all interfaces that pertain to it. This is a strategy commonly used by the Java Platform Libraries. In this circumstance, explicit casting to the type whose contract you want to use is exactly the right thing to do.

  • If the answer is clearly yes, then you should have both methods in your type ... but you should still ask yourself, "why am I not specifying my type with an interface"? In general, you should specify types with interfaces rather than abstract classes, but there are reasons to use the latter.

  • If the answer is somewhere in between, then you can consider specifying optional methods in your type. These are methods which are included in the contract of your type, but which implementing classes are not required to implement. Before Java 8, each implementing type would need to throw a UnsupportedOperationException for any optional methods that it did not implement. In Java 8, you can do something like this for optional methods:

======

public interface MyType {
    void contractOperation1();
    default void optionalOperation2() {
        throw new UnsupportedOperationException();
    }
}

A class that implements this interface will need to provide an implementation for contractOperation1(). However, the class will not need to provide an implementation for optionalOperation2() and if this method is invoked on an implementing class that has provided no implementation of its own, then the exception is thrown by default.

scottb
  • 9,908
  • 3
  • 40
  • 56
  • I think the Java 8 default method comes to my rescue here. – braaks55 Jan 08 '16 at 06:25
  • I specifically used an abstract class in the example code because I intended an 'is a'-relationship. These semantics get lost when using interfaces. Anyway, I cannot see how interfaces would solve my problem. I would still need an explicit cast or else loose the option of dynamic binding. Or am I missing something? – braaks55 Jan 08 '16 at 06:45
  • Inheritance is a powerful technique, but it also has problems. Inheritance breaks encapsulation and can make subclasses more fragile, less robust, and sometimes harder to maintain when subclassing across package boundaries. There are definitely cases where interface-based hierarchies offer a more robust solution than usual inheritance, even when an "is a" relationship exists. Moreover, it is still possible to base a type on an interface for which a skeletal implementation is provided in an abstract class (List interface -> AbstractList class -> ArrayList class) for example. – scottb Jan 08 '16 at 23:05
  • With regard to your second question, if you have decided that operation1 and operation2 are part of the contract of your type, then define operation2 in your abstract class to throw an `UnsupportedOperationException` and then override it in only those subclasses that provide new behavior for operation2. This gives you the abillity to invoke operation2 without casting. – scottb Jan 08 '16 at 23:13
  • Clear. Item closed as far as I am concerned. – braaks55 Jan 09 '16 at 10:49
-1

abstract class don't have the object,we just create the reference of that class and use it.

like: instead of this-

private AbstractClass classA = new ConcreteClassA();
private AbstractClass classB = new ConcreteClassB(); 

use this one

private AbstractClass classA;
private AbstractClass classB;

If we will create an object of the abstract class and calls the method having no body(as the method is pure virtual) it will give an error. That is why we cant create object of abstract class. Here is a similar StackOverflow question. In short, it is legal to have a public constructor on an abstract class.

more details are here:about abstraction instance

Community
  • 1
  • 1
Shrikant
  • 538
  • 5
  • 15