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 point I see different ways to invoke methods on Duck
and I can't decide which one is best practice.
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 in to the function happens to be castable to Flyer
and Speakable
, but I cringe when I see code like this because it does not allow compile time type checking and due to tight coupling it can throw unexpected exceptions for example when a differently typed object (not castable to either or one of the interfaces) is passed in as parameter, or if implementation of Duck
changes down the line so it no longer implements Flyer
.
I see Java code written like this all the time, sometimes in textbooks (for example pg. 300 of "Head First Design Patterns" by O'Reilly) so there must be a merit in it that I am missing.
If I were to write similar Code I would try to avoid downcasting to a type or interface that is not guaranteed. for example in this scenario 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!");
}
}
Which would 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 an unnecessary overkill? what are the pitfalls for doing this?
I feel like this design decouples the SafeSpeakAndFly()
function from its parameters and keeps nasty bugs at bay due to compile time type checking.
Why is the first method used so extensively in practice and the latter isn't?
I see Java code written like this all the time, sometimes in textbooks (for example pg. 300 of "Head First Design Patterns" by O'Reilly) so there must be a merit in it that I am missing.
This book was initially published back in 2004 and I don't think Java was supporting Generics at that time. So unsafe casting was something that was very commonly used then. Probably, if I didn't have the support of parametric polymorphism in Java, I would first check if the parameter is an instance of the type I'd like to cast it to and then do the actual cast:
private static void dangerousSpeakAndFly(Object x) {
if (x instanceof Speakable) {
Speakable temp = (Speakable) x;
temp.Speak();
}
if (x instanceof Flyer) {
Flyer temp2= (Flyer) x;
temp2.Fly();
}
}
Having Generics, however, lets us do this:
private static <T extends Speakable & Flyer> void reallySafeSpeakAndFly(T x) {
x.Speak();
x.Fly();
}
Here, the compiler can make sure we're not passing something that doesn't implement Speakable
and Flyer
and can detect such sassy attempts at compile-time.
Why is the first method used so extensively in practice and the latter isn't?
It might be that you've seen a lot of legacy code, I suppose. :)