Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java best practice: casting objects vs interfaces

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?

like image 910
ForeverStudent Avatar asked Mar 22 '16 15:03

ForeverStudent


People also ask

Is type casting good practice?

Business Impacts. Implied Typecasting is not extremely harmful to code but it is a sign of bad practice being displayed in development teams which can indicate symptoms of productivity issues.

What is the purpose of casting an object to another type in Java?

Type casting is a way of converting data from one data type to another data type. This process of data conversion is also known as type conversion or type coercion. In Java, we can cast both reference and primitive data types. By using casting, data can not be changed but only the data type is changed.

Is casting costly in Java?

To answer your questions. Up casting usually costs virtually nothing, (when you change the reference type to a parent class of the object). Knowledge of the reference type is enough to decide if uptyping is valid, it just gets the class loader to look up the inheritance map.

Can you cast an object in Java?

Objects of a class can be cast into objects of another class if both classes are related to each other through the property of inheritance, i.e., one class is the parent class, and the other class is the child class. This type of casting superclass object (parent class) will hold the sub-class object's properties.


2 Answers

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. :)

like image 179
Konstantin Yovkov Avatar answered Sep 28 '22 03:09

Konstantin Yovkov


You can enforce the argument to be at the same time Speakable and Flyer making a method generic with type intersection:

private <T extends Speakable & Flyer> static void DangerousSpeakAndFly(T x) { 
    // use any of `Speakable` or `Flyer` methods of `x`
}

thus you don't need casting nor creating additional interface.

like image 45
Alex Salauyou Avatar answered Sep 28 '22 03:09

Alex Salauyou