Suppose we have the following toy interfaces:
interface Speakable { public abstract void Speak(); } interface Flyer { public abstract void Fly(); }
and we have a class that implements both interfaces:
class Duck implements Speakable, Flyer { public void Speak() { System.out.println("quack quack don't eat me I taste bad."); } public void Fly() { System.out.println("I am flying"); } }
At this moment, I see different ways to call methods on Duck , and I can’t decide which one works best. Consider this scenario:
public class Lab { private static void DangerousSpeakAndFly(Object x) { Speakable temp = (Speakable) x; temp.Speak(); Flyer temp2= (Flyer) x; temp2.Fly(); } public static void main(String[] args) { Duck daffy= new Duck(); DangerousSpeakAndFly(daffy); } }
This program will behave as expected, because the object passed to the function can be hidden before Flyer and Speakable , but I compress when I see such code, because it does not allow checking the type of compilation time and because of the hard link, it may cause unexpected exceptions, for example, when an object with a different typification is passed as a parameter (not movable to any of the interfaces), or if the Duck implementation modifies the string so that it no longer implements Flyer .
I see Java code written this way all the time, sometimes in textbooks (for example, p. 300 of “Head First Design Patterns” O'Reilly), so there should be something in it that I am missing.
If I wrote similar code, I would try to avoid downcasting to a type or interface that is not guaranteed. for example, in this case, I would do something like this:
interface SpeakingFlyer extends Flyer, Speakable { } class BuzzLightyear implements SpeakingFlyer { public void Speak() { System.out.println("My name is Buzz"); } public void Fly() { System.out.println("To infinity and beyond!"); } }
What will allow me to do:
private static void SafeSpeakAndFly(SpeakingFlyer x) { x.Speak(); x.Fly(); } public static void main(String[] args) { BuzzLightyear bly= new BuzzLightyear(); SafeSpeakAndFly(bly); }
Is this too much? What are the pitfalls for this?
It seems to me that this construction separates the SafeSpeakAndFly() function from its parameters and prevents unpleasant errors due to checking the type of compilation time.
Why is the first method widely used in practice and the second not?