Win a copy of Head First Agile this week in the Agile forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic

Why Java needs explicit downcasting?  RSS feed

 
Rajdeep Biswas
Ranch Hand
Posts: 231
1
Eclipse IDE Java Opera
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I have seen other answers to similar questions but all of them rely on the fact that the language is defined to be like this. Following is what I am seeking an explanation to:

In an inheritance hierarchy, the parent types can hold child objects implicitly (why?), however for the child references to hold a parent object, explicit downcast is necessary (why?).



Please cite some example that explains why not doing this will fail, I mean using Animal, Dog type etc. If this question is already answered and I have missed it, citing that also will be helpful.
(Asked same in SO, but I like to have rancher's view always).

I just want to know how this lines make sense, other than just knowing they are language semantics.
 
Paweł Baczyński
Bartender
Posts: 2054
44
Firefox Browser IntelliJ IDE Java Linux Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Simply because every Dog is an Animal but not every Animal is a Dog.
 
Steffe Wilson
Ranch Hand
Posts: 165
12
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
If you define an object reference of type Animal (an) then the compiler allows that object reference access to visible members of the Animal class. (eat())

If you assign an actual Dog object to your an reference the compiler allows an.eat() and that's fine because Dog has inherited the eat() method because it extended Animal.

However, the reverse is not so fine...

If you define an object reference of type Dog (do) then the compiler allows that object reference access to visible members of the Dog class. (eat() and bark())

If you assign an actual Animal object to your do reference you end up with a reference that is allowed to access eat() and bark() BUT an actual Animal object does not have a bark() method, trying to call do.bark() would fail at run-time. To avoid the possibility of your program crashing at run-time the compiler forbids the assignment, unless you tell the compiler you will take full responsibility by applying a cast.
 
Junilu Lacar
Sheriff
Posts: 11172
160
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Rajdeep Biswas wrote:
In an inheritance hierarchy, the parent types can hold child objects implicitly (why?), however for the child references to hold a parent object, explicit downcast is necessary (why?).



Your example does not help you see the necessity of the cast on line 11 because you know that the animal you assigned to the variable on line 10 is actually a Dog. Java, however, does not make the same determination. Technically speaking, the Java compiler probably could make that determination in the same way that it can determine if a variable is effectively final and allow code like that to slide through. I doubt the language designers will ever do that though because unlike the determination about being effectively final, allowing code like that to compile without an explicit downcast will only encourage bad programming and design practice.

In this regard, Java is like that forgetful fish, Dory, in the Finding Nemo movie. It doesn't remember what happened on line 10 when it gets to line 11. All it knows is that it has a reference to an Animal and you're trying to assign that to a Dog. For the reasons already given above, the Java compiler needs some assurance that this is a legitimate assignment. The cast on line 11 is like the programmer saying to the compiler, "Trust me, I know what I'm doing and this Animal is definitely going to be a Dog," to give that assurance. With that assurance from the programmer, the compiler proceeds.

However, the runtime system is not as trusting. It will double check and throw a ClassCastException if the Animal is not actually a Dog, as is the case in this code:

In the above example, the cast on line 2 is required in order to make the code compile. At runtime, line 11 will execute fine because the "assurance" that the animal passed in actually is a Dog instance turns out to be true. However, when line 12 executes, the runtime system double checks the actual type and sees that it's not a Dog but a Cat, so it complains by throwing a ClassCastException to basically say "Hey, wait a minute! This object is supposed to be a Dog but I see that it's actually a Cat! Bad Programmer! You may have fooled the compiler but you can't fool me!"

The example above shows an abuse of downcasting because it can be "fraudulent" at runtime. This kind of code is BAD programming. Programs that do this kind of thing should be redesigned and programmers who write this kind of code should be "reeducated". However, there are legitimate uses of downcasting.

This article purports to give an example of a legitimate use of downcasting but the code example given is still a poor one, IMO, because while the mechanics shown are on track, the context given is still not quite right.

A better example would be if you were working with a class that extends a framework class, as you would be if you were working with an old Struts Action class, for example. Authors of frameworks and libraries need to write code that is as flexible as possible because there is no way for them to know how their code will be used out in the wild. It is up to the programmer who uses the framework to properly conform to the spirit of the framework design and doing so will sometimes make it necessary to make certain assurances to the Java compiler such as you would with a downcast.

Take this example of a typical method in a Struts Action class:

Here, MyAction is a subclass of the Struts Action class and MyForm is a subclass of the Struts ActionForm class. The custom myDispatchMethod() conforms to the signature of a method that the Struts framework can call, passing in the four parameters listed. However, since the form parameter is too general in nature, it's not very useful when the code represented by line 9 needs to access custom attributes of the MyForm class. Therefore, the downcast on line 7 is necessary and legitimate to the extent that the use of a framework extension necessitates it.

The JavaWorld article I cited above does give some good advice about downcasting though: Prefer polymorphism and dynamic binding over instanceof and downcasting.
 
Campbell Ritchie
Marshal
Posts: 55799
164
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Junilu Lacar wrote:. . . bad programming and design practice.
Good point. Lots of casts mean either a badly‑designed inheritance hierarchy or poor understanding of object‑oriented programming.


In this regard, Java is like that forgetful fish, Dory, in the Finding Nemo movie. . . .
That might be a bit unfair on the compiler. You have surely written a compiler yourself and will know how difficult it is to keep track of types at the best of times. Think how much more difficult it would be to follow all the execution and verify that the cast will be correct. Especially if there is user input and the type will be impossible to predict. The javac tool has to be “trusting”, and believe that the programmers know what they are doing
However, the runtime system is not as trusting. . . .
Because now it has all the information. It is no longer a case of believing that the cheque is in the post; it is a case of opening the envelope and seeing there is no cheque in it. The runtime does not “trust” because it can see everything, and knows that the cast was “fraudulent”. So you well deserve the class cast exception.
. . . programmers who write this kind of code should be "reeducated". . . .
Re-education, unless it is like something out of a Solzhenitsyn book, is too kind for them.
The JavaWorld article I cited above does give some good advice about downcasting though: Prefer polymorphism and dynamic binding over instanceof and downcasting.
If you start adding methods to a class, as you are doing with Animal→Cat and Animal→Dog, please consider whether the inheritance is correctly designed or not.
 
Campbell Ritchie
Marshal
Posts: 55799
164
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Junilu Lacar wrote:. . . purports to give an example of a legitimate use of downcasting but the code example given is still a poor one, . . .
Agree. That is a really good example of how not to do it. What they should have done is this:-That is polymorphism for you
 
Junilu Lacar
Sheriff
Posts: 11172
160
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:
Junilu Lacar wrote:In this regard, Java is like that forgetful fish, Dory, in the Finding Nemo movie
That might be a bit unfair on the compiler. You have surely written a compiler yourself and will know how difficult it is to keep track of types at the best of times.

Analogies eventually fall apart and I know this one does right away. The analogy was only meant to go as far as the fact that neither Dory nor the compiler have much of a long-term memory. In no way was it meant to malign the Java compiler

In the compiler's case, it doesn't attempt to remember all the local operations that might be related to a reference type check. All it knows is the immediate statement and what has already been established by type declarations in scope.

Yes, one of the things we had to do as CS students back in my day was to write a simple compiler. Ours had to be written using Pascal. Just plain old Pascal, not OO Pascal either

As I mentioned, the Java compiler could probably be "smart" and make the same kind of determination it does with effectively final variables but doing that for downcasting would just encourage bad programming habits and as such, it wouldn't be prudent or "smart" in the larger scheme of things, even if it were technically possible.
 
Rajdeep Biswas
Ranch Hand
Posts: 231
1
Eclipse IDE Java Opera
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Edit: I just was wondering that Dog inherits Animal and has all the behavior. Hence below code should have been allowed without explicit downcasting:

Or, that (I think I got it now) when I ask a Dog to store an Animal there are possibilities that I actually get a Cow or a Horse, because Animal being parent can hold any of its subtypes. If that's the case then why has Java allowed Animal to hold subtypes since there might be behavior typical to subtypes like a Dog will bark(), for that again compiler has to check and report. I know the rules, just trying to reason out in the simplest of sense.
 
Junilu Lacar
Sheriff
Posts: 11172
160
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Rajdeep Biswas wrote:If that's the case then why has Java allowed Animal to hold subtypes since there might be behavior typical to subtypes like a Dog will bark(), for that again compiler has to check and report. I know the rules, just trying to reason out in the simplest of sense.

This is the opposite way to think about it, that's why you're getting confused.

Of course you would allow Animal to reference ANY type of Animal. That's only logical. Is a Dog an Animal? Is a Cow an Animal? Is a Hippopotamus an Animal? The answer to these questions is YES! ALL of these are Animals. The "label" Animal is general and applies to all subtypes of Animal.

The reverse does not apply, however. Is a Cow a Dog? Is a Chicken a Dog? Is a Rabbit a Dog? The answer to these silly questions is NO, right? So why would Java allow you to say that ANY Animal can be considered a Dog?

It is up to YOU, the programmer, to think logically and write programs that are logical. Java is just safeguarding against programmers doing silly things that run counter to the logical relationships defined by a class hierarchy.
 
Rajdeep Biswas
Ranch Hand
Posts: 231
1
Eclipse IDE Java Opera
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Removing all the chains in my brain, if I simply think, it now looks like:
Parent can hold subtypes.
You mean thats the benefit of polymorphism and how its achieved.
Child types can hold parent but need explicit downcast.
Since polymorphism is allowed, this downcast is a safeguard.
 
Junilu Lacar
Sheriff
Posts: 11172
160
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Here's another way to think about it. Think of an opaque box (you can't see what's inside). All it says on the outside of the box is "Animal Inside". Now, without looking inside the box, can you guarantee that whatever is in it is a Dog? You can't right? Same thing with the Java compiler. All it knows is what has been declared in the program source, which is that the reference of type Animal will be to something that is some kind of Animal. That could be a Dog, Cat, Cow, Rabbit, Snake, Fish, Whale, etc.

Now suppose you, as a programmer, give your assurance that the Animal box is ALWAYS going to be a Dog, no matter what. That's what your doing with the downcast. You are giving your word to the compiler that the Animal in the box will indeed be a Dog. That's why the Java compiler will allow you to take a reference to an Animal and assign it to a reference to a Dog. In real-world terms, you have a label that says "Dog" and you're telling the Java compiler to trust that you know what you're doing when you want to put that label around the neck of whatever is in the box that says "Animal".

At runtime, however, Java isn't as easy to fool. The Java runtime looks inside the box labeled "Animal" and if it sees that the thing that's actually in there is anything other than an actual Dog, it will not allow you to put your "Dog" label on it. What if it's actually a Fish inside the Animal box? It would be wrong to try to put a sign that says "Dog" on a Fish, right? Or a Snake, or Rabbit, or Cow, or whatever else that's not a Dog.

 
Junilu Lacar
Sheriff
Posts: 11172
160
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Likes 2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Downcasting is a smell. It's suspicious code that goes against the logic of inheritance and polymorphism. The only reasonable use of downcasting is when you're dealing with frameworks, as I already explained and gave examples earlier.

DO NOT MAKE A HABIT of downcasting. It is usually a sign that your logic and/or design is faulty.
 
Rajdeep Biswas
Ranch Hand
Posts: 231
1
Eclipse IDE Java Opera
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That answers my question. Thanks a lot!
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!