Domanda

I am trying to figure out why do I need Downcasting. I reread my notes from collage and found the below example.

class Student {...}

class Graduate exteds Student {
   getResearchTopic(){...} // this method only exists in Graduate class.
}

We have a ref to Student class and want to access to getResearchTopic method;

Student s1 = new Graduate();
if(s1 instanceof Graduate){
   ((Graduate)s1).getResearchTopic();
}

Great example for Downcasting hah? My question is Why not declare s1 as a Graduate in the first place? Is there a real life example where I will have to downcast instead of using an instance of actual class?

È stato utile?

Soluzione

Well, you could have declared the reference s1 to be of type Graduate. The main benefit you get by declaring the reference of super type, is the power of polymorphism.

With a super type reference, pointing to a sub class object, you can bind the same reference to multiple sub class objects. And the actual method invoked will be decided at runtime, based on what object is being pointed to. But, the main condition for this is, that method should also be defined in the subclass, else the compiler will fail to find the method declaration.

Here, you were forced to downcast, because you haven't defined the method in the super class. As compiler cannot see the definition of that method in Student class. It has no idea about what the actual object s1 points to. Remember, compiler only checks the reference type to find the meethod declaration.

In general, whenever you see yourself downcasting to a subclass in your code, it is almost always a sign a something wrong (there are some exceptions though). And you should modify your classes.


Let's see what benefit you get by using a super class reference instead of a subclass reference:

For e.g: Suppose you have another sub class of Student as:

class Phd extends Student {
    getResearchTopic(){...}
}

and you also provide a definition (a default one) in Student class:

class Student {
    getResearchTopic(){...}
}

Now, you create a following two objects, both being pointed to by Student reference:

Student student = new Phd();
student.getResearchTopic();   // Calls Phd class method

student = new Graduate();
student.getResearchTopic();    // Calls Graduate class method

So, with only a single reference, you get to access methods specific to subclasses.


One major implementation of this feature you can see in factory method pattern, where a single static method returns an object of different sub classes based on some condition:

public static Student getInstance(String type) {
    if (type.equals("graduate")) 
        return new Graduate();
    else if (type.equals("phd"))
        return new Phd();
}

So, you can see that the same method returns an object of different subclasses.

All of the above stuffs you can do just because of one concept:

A Super class reference can refer to any sub class objects, but not vice-versa.

Altri suggerimenti

Say you have a method that takes a Student as a parameter. Most of the things it does are generic for all students. But if it is a Graduate there might be something else it does as well. In that case you would need to determine if the Student passed in was actually a Graduate and do some special logic in that instance.

Maybe something like this:

class StudentDAO {
    public void SaveStudent(Student s) {
       // Do something to save the student data to a database.

       if ( s instanceof Graduate ) {
           // Save their research topic too.
       }
    }
}

Note that doing that kind of thing is usually a poor programming practice, but sometimes it makes sense.

When you deserialize an object using the default Java deserializer, you use this code (and you use analogous code when using another deserializer, e.g. the Jackson JSON deserializer)

ObjectInputStream ois = new ObjectInputStream(in);
Object obj = ois.readObject();

You then need to cast obj to its actual type, because readObject() will always return a plain old Object - the method can't statically verify what sort of object is being read

In cases where you want to use polymorphism, it would be nice to work with Student objects, and then "downcast" to use methods specific to Graduate objects.

In general, if you have a method that works with Student objects, that method don't really know at compile-time what specific type of Student objects are passed in. Thus, at run-time, the method should check for the specific type and process accordingly.

Downcasting does help when you're trying to make generic methods. For example, I often see code that parses an XML String into an Object. The Object can then be downcast into the specific Object that you (as the coder) know it represents.

private static XStream xstream = new XStream(new DomDriver());
static {
    xstream.processAnnotations(MyFirstClass.class);
    xstream.processAnnotations(MySecondClass.class);
    // ...
}

public static Object fromXML(String xml) {
    return xstream.fromXML(xml);
}

This lets me make a very generic method which does what I want it to do in all cases. Then, when I call it, I can simply downcast the Object into what I know it's going to be. It prevents me from having to make a separate parse method for every object type and improves the readability of my code.

MyFirstClass parsedObject = (MyFirstClass) MyXMLTransformer.fromXML(xml);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top