12

My question is about a special case of the super class Animal.

  1. My Animal can moveForward() and eat().
  2. Seal extends Animal.
  3. Dog extends Animal.
  4. And there's a special creature that also extends Animal called Human.
  5. Human implements also a method speak() (not implemented by Animal).

In an implementation of an abstract method which accepts Animal I would like to use the speak() method. That seems not possible without doing a downcast. Jeremy Miller wrote in his article that a downcast smells.

What would be a solution to avoid downcasting in this situation?

  • 6
    Fix your model. Using existing taxonomical hierarchy to model a class hierarchy is usually wrong idea. You should pull your abstractions from existing code, not create abstractions and then try to fit code to it. – Euphoric Jun 17 '14 at 15:47
  • 2
    If you want Animals to speak, then the ability to speak is part of what makes something an animal: artima.com/interfacedesign/PreferPoly.html – JeffO Jun 17 '14 at 15:54
  • 8
    moveForward? what about crabs? – Fabio Marcolini Jun 17 '14 at 18:20
  • Which item # is that in Effective Java, BTW? – goldilocks Jun 17 '14 at 19:15
  • 1
    Some people can't speak, so an exception is thrown if you try. – Tulains Córdova Jan 7 '16 at 20:26
12

If you have a method that needs to know whether the specific class is of type Human in order to do something, then you are breaking some SOLID principles, particularly :

  • Open/closed principle - if, in the future, you need to add a new animal type that can speak (for example, a parrot), or do something specific for that type, your existing code will have to change
  • Interface segregation principle - it sounds like you are generalizing too much. An animal can cover wide spectrum of species.

In my opinion, if your method expects a particular class type, to call it's particular method, then change that method to accept only that class, and not it's interface.

Something like this :

public void MakeItSpeak( Human obj );

and not like this :

public void SpeakIfHuman( Animal obj );
  • One could also create an abstract method on Animal called canSpeak and each concrete implementation must define whether or not it can "speak". – Brandon Jun 18 '14 at 2:16
  • But I like your answer better. Much complexity comes from trying to treat something like something that it is not. – Brandon Jun 18 '14 at 2:16
  • @Brandon I guess, in that case, if it can not "speak", then throw an exception. Or just do nothing. – BЈовић Jun 18 '14 at 4:20
  • So you get overloading like this: public void makeAnimalDoDailyThing(Animal animal) {animal.moveForward(); animal.eat()} and public void makeAnimalDoDailyThing(Human human) {human.moveForward(); human.eat(); human.speak();} – Bart Weber Jun 18 '14 at 9:03
  • 1
    Go all the way - makeItSpeak( ISpeakingAnimal animal ) - you can then have ISpeakingAnimal implements Animal. You could also have makeItSpeak( Animal animal ){ speak if instanceof ISpeakingAnimal }, but that has a faint smell. – ptyx Jun 18 '14 at 20:53
5

The problem is not that you are downcasting - it's that you are downcasting to Human. Instead, create an interface:

public interface CanSpeak{
    void speak();
}

public abstract class Animal{
    //....
}

public class Human extends Animal implements CanSpeak{
    public void speak(){
        //....
    }
}

public void mysteriousMethod(Animal animal){
    //....
    if(animal instanceof CanSpeak){
        ((CanSpeak)animal).speak();
    }else{
        //Throw exception or something
    }
    //....
}

This way, the condition is not that the animal is Human - the condition is that it can speak. This means mysteriousMethod can work with other, non-human subclasses of Animal as long as they implement CanSpeak.

  • in which case it should take as argument an instance of CanSpeak instead of Animal – Newtopian Jun 18 '14 at 20:28
  • 1
    @Newtopian Assuming that that method doesn't need anything from Animal, and that all users of that method will hold the object they want to sent to it via a CanSpeak type reference(or even Human type reference). If that was the case, that method could have used Human in the first place and we wouldn't need to introduce CanSpeak. – Idan Arye Jun 18 '14 at 20:40
  • Getting rid of the interface is not linked to being specific in the method signature you can get rid of the interface if and only if there is only Humans and will ever be only humans that "CanSpeak". – Newtopian Jun 18 '14 at 20:55
  • 1
    @Newtopian The reason we introduced CanSpeak in the first place is not that we have something that implements it(Human) but that we have something that uses it(the method). The point of CanSpeak is to decouple that method from the concrete class Human. If we didn't have methods that to treat things that CanSpeak differently, there would be no point in distinguishing things that CanSpeak. We don't create an interface just because we have a method... – Idan Arye Jun 18 '14 at 21:11
2

In this case, the default implementation of speak() in the AbstractAnimal class would be:

void speak() throws CantSpeakException {
  throw new CantSpeakException();
}

At that point, you've got a default implementation in the Abstract class - and it behaves correctly.

try {
  thingy.speak();
} catch (CantSeakException e) {
  System.out.println("You can't talk to the " + thingy.name());
}

Yes, this means you've got try-catches scattered through the code to handle every speak, but the alternative to this is if(thingy is Human) wrapping all the speaks instead.

The advantage of the exception is that if you have another type of thing at some point that speaks (a parrot) you won't need to reimplement all of your tests.

  • 1
    I don't think this is a good reason to use exception (unless it is python code). – Bryan Chen Jun 18 '14 at 1:39
  • 1
    I won't down vote because this would technically work, but I really don't like this sort of design. Why define a method at a parent level that the parent cannot implement? Unless you know what type the object is (and if you did - you would not have this problem), you need to always use a try/catch to check for a completely avoidable exception. You could even use an canSpeak() method to handle this better. – Brandon Jun 18 '14 at 2:19
  • 2
    I have worked on a project that had a ton of defects originating from somebody's decision to rewrite a bunch of working methods to throw an exception because they are using an obsolete implementation. He could have fixed the implementation, but chose instead to throw exceptions everywhere. Naturally, we entered QA with hundreds of bugs. So I am biased against throwing exceptions in methods that are not supposed to be called. If they aren't supposed to be called, delete them. – Brandon Jun 18 '14 at 2:22
  • 2
    Alternative to throwing an exception in this case may be doing nothing. After all, they can not speak :) – BЈовић Jun 18 '14 at 4:23
  • @Brandon there's a difference between designing it from the start with the exception vs retrofitting it in later. One could also look at an interface with a default method in Java8, or not throwing an exception and just being silent. The key point though is that to avoid needing to downcast, the function needs to be defined in the type being passed. – user40980 Jun 18 '14 at 17:34
1

You could add Communicate to Animal. Dog barks, Human speaks, Seal.. uhh.. I don't know what seal does.

But it sounds like your method is designed to if(Animal is Human) Speak();

The question you may want to ask is, what is the alternative? Its hard to give a suggestion since I don't know exactly what you want to achieve. There are theoretical situations where downcasting/upcasting is the best approach.

  • 15
    The seal goes ow ow ow, but the fox is undefined. – user40980 Jun 17 '14 at 16:10
1

Downcasting is sometimes necessary and appropriate. In particular, it's often appropriate in cases where one has objects that may or may not have some ability, and one wishes to use that ability when it exists while handling objects without that ability in some default fashion. As a simple example, suppose a String is asked whether it is equal to some other arbitrary object. For one String to equal another String, it must examine the length and backing character array of the other string. If a String is asked whether it equals a Dog, however, it cannot access the length of the Dog, but it shouldn't have to; instead, if the object to which a String is supposed to compare itself isn't a String, the comparison should use a default behavior (reporting that the other object isn't equal).

The time when downcasting should be regarded as most dubious is when the object being cast is "known" to be of the proper type. In general, if an object is known to be a Cat, one should use a variable of type Cat, rather than a variable of type Animal, to refer to it. There are times when this doesn't always work, however. For example, a Zoo collection might hold pairs of objects in even/odd array slots, with the expectation that the objects in each pair will be able to act upon each other, even if they cannot act upon the objects in other pairs. In such a case, the objects in each pair would still have to accept a non-specific parameter type such that they could, syntactically, be passed the objects from any other pair. Thus, even if Cat's playWith(Animal other) method would only work when other was a Cat, the Zoo would need to be able to pass it an element of an Animal[], so its parameter type would have to be Animal rather than Cat.

In cases where downcasting is legitimately unavoidable, one should use it without qualm. The key question is determining when one can sensibly avoid downcasting, and avoiding it when sensibly possible.

  • In the string case, you should rather have a method Object.equalToString(String string). Then you have boolean String.equal(Object object) { return object.equalStoString(this); } So, no downcast needed: you can use dynamic dispatching. – Giorgio Jun 17 '14 at 17:39
  • @Giorgio: Dynamic dispatching has its uses, but it's generally even worse than downcasting. – supercat Jun 17 '14 at 18:16
  • way dynamic dispatching generally even worse than downcasting? i thoughh it is other way around – Bryan Chen Jun 18 '14 at 1:37
  • @BryanChen: That depends on your terminology. I don't think Object has any equalStoString virtual method, and I'll admit I don't know how the quoted example would even work in Java, but in C#, dynamic dispatch (as distinct from virtual dispatch) would mean that the compiler essentially has to do Reflection-based name lookup the first time a method is used on a class, which is distinct from virtual dispatch (which simply makes a call via a slot in the virtual method table which is required to contain a valid method address). – supercat Jun 18 '14 at 1:41
  • From a modelling point of view, I would prefer dynamic dispatching in general. It is also the object-oriented way of selecting a procedure based on the type of its input parameter(s). – Giorgio Jun 18 '14 at 10:46
1

In an implementation of an abstract method which accepts Animals I would like to use the speak() method.

You have a few choices:

  • Use reflection to call speak if it exists. Advantage: no dependency on Human. Disadvantage: there is now have a hidden dependency on the name "speak".

  • Introduce a new interface Speaker and downcast to the interface. This is more flexible than depending on a specific concrete type. It has the disadvantage that you have to modify Human to implement Speaker. This won't work if you can't modify Human

  • Downcast to Human. This has the disadvantage that you will have to modify the code whenever you want another subclass to speak. Ideally you want to extend applications by adding code without repeatedly going back and changing old code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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