8

I have several classes Parent and Child1 ... Child9 implemented in Java. Parent is an abstract class, containing all the common variables of the child classes (a lot, which is the main reason why I made Parent an abstract class and not an interface), some abstract and some implemented methods.

Some of the child classes have custom methods which are specific to them. So often I find myself calling a child method using downcasting:

Parent p = new Child1();
((Child1) p).child1SpecificMethod();

Somehow I have the feeling that this is bad OOD practice, but I am not quite sure if that really is the case respectively how to improve the design.

- Edit - What I probably should change anyway is the fact that I use the Parent class for organizing many (for now) common variables, making them (or a container object) members of the concrete classes.

  • 6
    If you know, that the value of p is of type Child1, why not make p of type Child1, thus no need to cast? – Benni Oct 10 '14 at 8:49
  • 2
    Generally yes, this is bad practice. If you find yourself commonly downcasting things, than you've probably done something wrong. However it's really hard to say more without more information about your situation. – Aviv Cohn Oct 10 '14 at 9:00
  • 4
    Why are you declaring p as a Parent in the first place? Sticking to general types is good practice for APIs and return types, but not within the same scope where you yourself allocate a concrete class. – Kilian Foth Oct 10 '14 at 9:01
  • Looks like you're shoehorning inheritance into your use case. If the children cannot use inheritance to take advantage of a common parent, you're going to need to review your class design – kolossus Oct 10 '14 at 18:18
  • 2
    possible duplicate of How to avoid downcasting? – gnat Jan 7 '16 at 20:18
11

This is not only bad practice, this is unnecessary complicated.

Why do you use inheritance in general?

When you use inheritance, you have a common set of behaviour, which you want to make available for many different carriers. This includes class inheritance as well als interface inheritance. The heir, so to speak, is oftentimes a specialization of the class from whom it inherits; which is mainly true for class inheritance.

Think of a class car and a subclass porsche (the typical is a-relationship). You have general behaviour like starting/stopping engine, steering and so on. If you treat a porsche like a car, you are bound to this aspect of its behaviour. If you know, that you only want a porsche and only deal with it as a porsche, it is redundant to instantiiate a porsche as a car and get porsche-behaviour via casting.

Polymorphism makes sense the other way around:

You have a porsche and need to treat it from the aspect of a car; e.g. driving

As long as your porsche accepts steer left, steer right, shift up, shift down, etc. you could make use of polymorphism/substitution one for the other.

It is better, to instantiate your objects in their specialized form. Then you could make the most of polymorphism and use it only when you need it.

That said: Parent p = new Child1(); makes no sense for me.

Edit: I would implement porsche different (via composition), but for the sake of the example, it is a car.

4

Your feeling is right to consider it a bad practise. Imagine your example code a little different:

Parent p = createObject();
((Child1) p).child1SpecificMethod();

How do you know, that the value of p is really Child1? You don't and this can cause a ClassCastException at runtime. If you need to call child1SpecificMethod() in your code you have to make sure that p is of type Child1. If that is not possible because Object p is passed to your code (e.g. as a method parameter) as type Parent, you could consider using a variant of the Visitor-Pattern and execute child1SpecificMethod in the handle-Method of your visitor object, that handles Child1.

  • "you have to make sure that p is of type Child1" - that is indeed not possible because Object p is passed as method parameter. The visitor pattern is good hint. – jpmath Oct 10 '14 at 13:14
4

Use lookup of capabilities instead. Give no access to the child classes, consider them implementations of the Parent class.

Then define interfaces specifying some capability, feature.

interface Child1Specific {
    void child1SpecificMethod();
}

Usage:

Parent parent = ...
Child1Specific specific = parent.lookup(Child1Specific.class);
if (specific1 != null) {
    specific1.child1SpecificMethod();
}

This discovery mechanism is very flexible. Using delegation instead of inheritance can be quite rewarding. Note, having child classes is no longer needed.

Or in java 8 (where several variations are possible, and the interface could be functional too):

Optional<Child1Specific> specific = parent.lookup(Child1Specific.class);
if (specific1.isPresent()) {
    specific1.get().child1SpecificMethod();
}

Make in the Parent class a lookup for some capability:

public class Parent {
    protected final Map<Class<?>, Object> capabilities = new HashMap<>();
    protected final <T> void registerCapability(Class<T> klass, T object);

    public <T> T lookup(Class<T> klass) {
        Object object = capabilities.get(klass);
        return object == null ? null : klass.cast(object);
    }

Or in java 8:

    public <T> Optional<T> lookup(Class<T> klass) {
        Object object = capabilities.get(klass);
        return Optional.ofNullable(klass.cast(object));
    }

The child class:

class Child1 extend Parent implements Child1Specific {
    Child1() {
        registerCapability(Child1Specific.class, this);
    }
}

Or more dynamic:

class Child1 extends Parent {
    private Child1Specific specific = new Child1Specific() {
        ... Parent.this ...
    };
    Child1() {
        registerCapability(Child1Specific.class, specific);
    }
}
-3

Add an abstract method child1SpecificMethod() in Parent class(you need to mark class as abstract) and provide its implementation in respective child class.

protected by gnat Jan 24 '17 at 21:50

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.