Upcasting vs downcasting.

R

R. Clayton

Why isn't downcasting more like upcasting?

I can directly assign a reference to a variable when the reference type
(dynamic type, actual type) is a descendant of the variable type (static type,
apparent type):

Parent p = new Child();

I can't directly assign a reference to a variable when the reference type
is a proper ancestor of the variable type; I need to use a cast (downcast):

Child c = (Child) p;

Why is the explicit downcast necessary? It doesn't seem to add any useful
information: p's dynamic type has to be compatible with c's static type, and
all that information can be gathered without the cast. I guess it allows
over-downcasting, as in (assuming a suitable assignment to p)

Child c = (GrandChild) p;

but that seems a rather obscure reason to force downcasting everywhere, and
does it matter anyway? There's only c's static and dynamic types; if p
contains a reference to a GrandGrandChild instance, the GrandChild-cast
information seems to disappear (e.g., c instanceof GrandGrandChild is true,
where it should be false if p's reference were treated as a GrandChild,
assuming the obvious inheritance hierarchy).
 
S

Stefan Ram

Child c = (Child) p;
Why is the explicit downcast necessary?

So that the programmer acknowledges that he is aware
of the run-time cost?

In another case, he has to acknowledge that he is
aware of the loss of precission:

int i =( int )doubleValue;
 
M

Mike Schilling

R. Clayton said:
Why isn't downcasting more like upcasting?

I can directly assign a reference to a variable when the reference
type (dynamic type, actual type) is a descendant of the variable type
(static type, apparent type):

Parent p = new Child();

I can't directly assign a reference to a variable when the reference
type
is a proper ancestor of the variable type; I need to use a cast
(downcast):

Child c = (Child) p;

Why is the explicit downcast necessary?

The first assignment (Parent p = new Child();). will always succeed. The
second (Child c = (Child) p;) will fail if p is not in fact a child.
Requiring the explicit cast

1. Ensures that the author of the code realizes that he's written
something that, on the surface, at least, might fail.
2. Alerts readers of the code of the same thing.

The point of generics is to remove the need for explicit casts in situations
where there isn't really any danger, by providing additional information to
the compiler. E.g.

Foo s = Foo.class.newInstance(); // prior to 1.5, this required a cast
List<String> list;
String s = list.get(0); // prior to 1.5, this required a cast too
 
A

Arne Vajhøj

R. Clayton said:
Why isn't downcasting more like upcasting?

I can directly assign a reference to a variable when the reference type
(dynamic type, actual type) is a descendant of the variable type (static type,
apparent type):

Parent p = new Child();

I can't directly assign a reference to a variable when the reference type
is a proper ancestor of the variable type; I need to use a cast (downcast):

Child c = (Child) p;

Why is the explicit downcast necessary? It doesn't seem to add any useful
information: p's dynamic type has to be compatible with c's static type, and
all that information can be gathered without the cast.

The first is good code utilizing polymorphism.

The last code is a hack that violates good OOP and can potentially
result in a ClassCastException.

You should be happy that they allow the last one !

Arne
 
J

Joshua Cranmer

R. Clayton said:
I can't directly assign a reference to a variable when the reference type
is a proper ancestor of the variable type; I need to use a cast (downcast):

Child c = (Child) p;

Why is the explicit downcast necessary? It doesn't seem to add any useful
information: p's dynamic type has to be compatible with c's static type, and
all that information can be gathered without the cast.

Casting among reference types in Java needs to be explicit for downcasts
because the compiler cannot prove that the cast will always succeed.
Each statement in Java is treated in isolation, so the compiler is in
effect reading this:

Parent p;
<great big mash of stuff>
Child c = (Child) p;
<great big mash of stuff>

It doesn't know what p is when parsing the statement, so it doesn't know
that it is in fact containing an instance of Child when compiling.

If you're asking, why require a cast even though it can't prove it, the
simplest reason is that it would be an unsafe cast. It's not guaranteed
to work all the time, or even most of the time. Having the programmer
make the cast is essentially a contract between the programmer and the
compiler saying that the programmer knows what he or she is doing,
acknowledges the risks of his or her actions, and is prepared to suffer
the consequences of a mistake.
> I guess it allows
over-downcasting, as in (assuming a suitable assignment to p)

Child c = (GrandChild) p;

but that seems a rather obscure reason to force downcasting everywhere, and
does it matter anyway?

I don't see why one would think that allowing your so-called
"over-downcasting" would "force" downcasting everywhere...
> There's only c's static and dynamic types; if p
contains a reference to a GrandGrandChild instance, the GrandChild-cast
information seems to disappear (e.g., c instanceof GrandGrandChild is true,
where it should be false if p's reference were treated as a GrandChild,
assuming the obvious inheritance hierarchy).

I think you have a mistaken notion of what the downcasting represents. A
variable's type in Java is mostly for the purpose of the compiler; the
JVM will only recognize five types: int (which represents boolean, byte,
char, short, and int), float, double, long, and reference [1]. Since the
type is mostly important at compile-time only for the compiler's
convenience anyways, what happens at runtime is only important for
determining whether a call will succeed or fail.

It's more helpful to think of it like this: the compiler refuses to put
an object in a container of a different type. Casting is your way of
telling the compiler, "I know more than you do (because I have more
information), so I can guarantee that this Parent expression is really a
Child expression," to which the compiler responds, "okay, I'll let you
treat it as a Child, but I'm going to double-check that it's true at
runtime [via a checkcast instruction]."

So the cast is performing more of an instruction to the compiler than it
is to the JVM (since a call actually requiring a Child instance would
fail, albeit a bit more catastrophically).

[1] To be more precise, this only applies to types on the stack or in
local variable slots.
 
E

Eric Sosman

R. Clayton said:
Why isn't downcasting more like upcasting?

I can directly assign a reference to a variable when the reference type
(dynamic type, actual type) is a descendant of the variable type (static type,
apparent type):

Parent p = new Child();

I can't directly assign a reference to a variable when the reference type
is a proper ancestor of the variable type; I need to use a cast (downcast):

Child c = (Child) p;

Why is the explicit downcast necessary? [...]

You haven't said so, but I'll assume `Child extends Parent'.
If so every Child is a Parent, so `Parent p = new Child()' is
unobjectionable and unsurprising, and Java accepts it without
complaint. But it does not follow that every Parent is a Child
(we ignore all discussions of Chicken and Egg), so it is possible
that a reference-to-Parent could refer to something that was not
a Child, so `Child c' could not refer to it.

Java "knows" only that `p' is null or refers to something
that is a Parent, "is a" taken to include subclasses of Parent.
*You* may know that `p' refers to a Parent that is also a Child,
but Java doesn't know this. You communicate your knowledge to
Java by writing the cast, which says "Java, believe me: for
reasons beyond your feeble intellect I happen to know that `p'
at this moment refers to a Child (or subclass of Child), so this
is perfectly safe." Java answers "Okay, boss, whatever you say
(but I'll double-check when the assignment actually occurs, and
if the boss is wrong I'll go on strike with ClassCastException)."

Every Dachshund is a Dog, so it's always safe to convert a
Dachshund reference to a Dog reference. But a Dog reference might
refer to a Chihuahua, which is *not* a Dachshund. That is, there
exist Dog references that cannot become Chihuahua references, and
Java wants your reassurance before it will permit the conversion.
 
D

Daniel Pitts

R. Clayton said:
Why isn't downcasting more like upcasting?

I can directly assign a reference to a variable when the reference type
(dynamic type, actual type) is a descendant of the variable type (static type,
apparent type):

Parent p = new Child();

I can't directly assign a reference to a variable when the reference type
is a proper ancestor of the variable type; I need to use a cast (downcast):
The downcast is an acknowledgment by the programmer that they are sure
that the dynamic type is assignable to the static type. It forces the
programmer to carefully consider if this is indeed true. Down casting
has bad side-effects if the type is wrong (in Java, a
ClassCastException, in C++, the ever-looming Undefined Behavior)

Since the up-cast is safe, and the down-cast needs care, it makes sense
that you should jump an extra loop.

Otherwise, you have a nearly untyped language.
 
M

Mark Space

R. Clayton said:
Why isn't downcasting more like upcasting?

I can directly assign a reference to a variable when the reference type
(dynamic type, actual type) is a descendant of the variable type (static type,
apparent type):

Parent p = new Child();

I can't directly assign a reference to a variable when the reference type
is a proper ancestor of the variable type; I need to use a cast (downcast):

Child c = (Child) p;

Why is the explicit downcast necessary? It doesn't seem to add any useful
information: p's dynamic type has to be compatible with c's static type, and


Nope, not true.


class Parent {}
class Child extends Parent {}
class Bastard extends Parent {}

Parent p = new Bastard();

Child c = (Child) p;

This fails at runtime.

As mentioned, the usual rational for making you type the (Child) is to
make certain that you know it may in fact fail. I suppose the language
designers could have omitted it. However, I also suppose there could be
more than one appropriate class to cast to. Making an explicit cast is
just easier on the compiler, and results in no surprises for the programmer.
 
R

Roedy Green

Why isn't downcasting more like upcasting?

http://mindprod.com/jgloss/cast.html
--
Roedy Green Canadian Mind Products
http://mindprod.com

"By 2040, the Sahara will be moving into Europe, and Berlin
will be as hot as Baghdad. Atlanta will end up a kudzu
jungle. Phoenix will become uninhabitable, as will parts of
Beijing (desert), Miami (rising seas) and London (floods).
Food shortages will drive millions of people north, raising
political tensions."
~ James Lovelock
Lovelock is more pessimistic than the consensus, because he thinks man will
refuse to take significant action to ameliorate global warming.
 
M

Michael Jung

Why isn't downcasting more like upcasting? [...]
Child c = (Child) p;

Why is the explicit downcast necessary? It doesn't seem to add any useful
information: p's dynamic type has to be compatible with c's static type, and
all that information can be gathered without the cast.
[...]

In this case it can, but in some expressions an explicit downcast is
needed, e. g.:

inherit((Child)p);

So in general the information can not be gathered without a cast.
What remains would be syntactical sugar for some cases. And such is
dubious as others have pointed out.

Michael
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,146
Latest member
Vinay KumarNevatia_
Top