It's not a secret anymore!*
The moose likes Programmer Certification (SCJP/OCPJP) and the fly likes Ploymorphism code in this example? Please Explain. Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Certification » Programmer Certification (SCJP/OCPJP)
Bookmark "Ploymorphism code in this example? Please Explain." Watch "Ploymorphism code in this example? Please Explain." New topic
Author

Ploymorphism code in this example? Please Explain.

satya mamillapalli
Ranch Hand

Joined: Jun 22, 2005
Posts: 63
Class A { int f()}
Class B extends A { int f()}
Class C extends B { int f()}

A ref1 = new C(); -> This is o.k. because A is supercalss of C.

B ref2 = (B) ref1; <--- What will happen here exactly..(It looks like downcasting from C to B.But how is it allowed?

Can somebody Please explain..
Rick O'Shay
Ranch Hand

Joined: Sep 19, 2004
Posts: 531
Originally posted by satya mamillapalli:
Class A { int f()}
Class B extends A { int f()}
Class C extends B { int f()}

A ref1 = new C(); -> This is o.k. because A is supercalss of C.

B ref2 = (B) ref1; <--- What will happen here exactly..(It looks like downcasting from C to B.But how is it allowed?

Can somebody Please explain..


You are downcasting a C to a B which is perfectly reasonable. That cast is required because the compiler does not know the specific type of the object being referred to with the A reference. In this example the object is in fact a C so not only will it compile it will run without error: C IS-A B.
Rick O'Shay
Ranch Hand

Joined: Sep 19, 2004
Posts: 531
Correction, you are downcasting an A reference to a B reference where the object is of type C. Loosely speaking you are casting a C to a B as stated however that's at the object level and that's an upcast conceptually. Main point is that you have to cosider reference types and object types independently to get a picture of what's going on.
Philip Heller
author
Ranch Hand

Joined: Oct 24, 2000
Posts: 119
I think of it like this: objects have class, references have type. (A type is a class, an interface, or an enum.)

The type of a reference is the type that appears in its declaration. In your line

they type of ref2 is B, because that's how it's declared.

The class of an object is determined by which constructor got called when the object was created. In your line

the object's class is C. Note that I don't say "the class of ref1 is C", because ref1 is a reference, not an object.

When you compile, the compiler has no knowledge about the class of any object. The compiler is only aware of the references and types. (Makes sense ... objects don't exist until the program runs. The compiler doen't make any guesses about what the class of an object pointed to by some reference is going to be.

So when you copile this line:
The compiler only cares about two issues:

1) On the right-hand side of the "=", is it ok to cast ref1 to a B? This check only cares about the type of ref1, not about the (as yet unknown) class of the underlying object.
2) Are the types of the references on both sides of the "=" compatible?

Since both answers are "yes", the compiler is happy. Of course, the compiler's knowledge is limited. At run time, it may turn out that the class of the underlying object is incompatible with the new type. If this happens, the JVM will throw ClassCastException.

The moral: when you cast, you force the compiler to make a conversion that involves some run-time risk. In casting primitives, the risk is loss of numerical precision. In casting references, the risk is a ClassCastException.

Hope this helps.

-- Phil


Consultant to SCJP team.<br />Co-designer of SCJD exam.<br />Co-author of "Complete Java 2 Certification Study Guide".<br />Author of "Ground-Up Java".
satya mamillapalli
Ranch Hand

Joined: Jun 22, 2005
Posts: 63
Phil,

Thanks for your explanation..

But still I was not clear about point "1) On the right-hand side of the "=", is it ok to cast ref1 to a B? This check only cares about the type of ref1, not about the (as yet unknown) class of the underlying object."

ref1 is of type "A"

A extends B so A can cast to a B but not the other way around..

How can we cast this reference to "B".Please explain If I am missing some thing here?

Even If I do the following it compiles.

ref1 = (C) ref1; <-- What exactly happening here?

ref1 = (B) ref1; <-- What exactly happening here?

BUT If I do ref1 = (D) ref1;(D is a stand alone class) I got the compiler error..

SO, Is it like These references have to be in the inheritance heirarchy, does not matter which direction.

For objects , Only Upward casting works..

Please Advise..

Thanks
Satya
[ August 07, 2005: Message edited by: satya mamillapalli ]
Jon Egan
Ranch Hand

Joined: Mar 24, 2004
Posts: 83
Satya,

One line of what you wrote in your last post is not accurate - you wrote "A extends B so A can cast to a B...". In fact, A does not extend B... just the opposite: B extends A. "extends" is a one-way relationship.

I think of classes which extend other classes (or, for that matter, implement interfaces) in a Venn diagram kind of way. Let's look at your three classes in that way (unfortunately, in text, because I'm not going to try to draw an ASCII-art venn diagram):


Draw a circle and label it A. This represents all of the objects which can be assigned to a reference of type A.

Draw another circle, entirely within the 'A' circle, and label it B. This represents anything that can be assigned to a reference of type B. Notice that anything that can be assigned to a 'B' reference can also be assigned to an 'A' reference... but the opposite is not true (there is room outside the 'B' circle, still within the 'A' circle, for objects whose class is 'A').

and so on.... Every C object can be assigned to a reference of type A or B, but objects of class 'A' or 'B' cannot be assigned to references of type 'C'.

(By the way, if you are trying to envision which fields/methods are accessible in an object of one of these types, a Venn diagram with the circles in the other order is a useful mental image.)

Now, as far as the compiler is concerned, your two lines:

can't be trusted to happen as a single unit... maybe somebody snuck in an operation in between (perhaps another thread?) and swapped out the 'C' object in the ref1 reference for another object that also fits in an 'A' type reference. The only thing the compiler knows about the object in ref1 on line 2 is that it's something that fits in an 'A' reference - it "IS-A" A (could be an 'A' object, or a 'B' or 'C' object). But it cannot be a 'D' object, because there's no way anyone ever managed to get a 'D' into an 'A' reference (I'll come back to this).

So, since line 2 is trying to assign "whatever object is referenced by ref1" (remember, could be any of A, B, or C) into a reference of type 'B'. The compiler knows that this might be perfectly legal (if it's a B or C object), or it might not (if it's an 'A'). Since the compiler is "uncertain", you have to include the cast. If it turns out the compiler was right to worry (if, for example, line 1 created an 'A' instead), you will get the ClassCastException at runtime.

I said I'd come back to "it cannot be a 'D' object". When the compiler is looking at line 2, it knows that the object in ref1 might be anything that "IS-A" A (any of A, B, or C). It also knows that the object will not be anything that fails the "IS-A" test (such as a D). So when you try to cast the object in ref1 to a D type, the compiler can be certain that this line can't work. It gives the compiler error to save you the trouble of the inevitable ClassCastException at runtime.

hope this helps,
Jon
[ August 07, 2005: Message edited by: Jon Egan ]
satya mamillapalli
Ranch Hand

Joined: Jun 22, 2005
Posts: 63
Thanks Jon for your Venn Diagram concept..I understood Line 1 clearly now..

Still I am uncertain about the following .


B ref1 = new C(); <- ref1 type is B Object's class is C and so C can be assigned to B.

According to the explanation, ref1 can only take either B or C(ref1 "IS A" B or C), How can the following cast (A) ref1 works..

A ref2 = (A) ref1; <- ref1 type is B How can this be cast to type A.Should n't it throw compiler error.,

Please Advise..

Thanks
Satya.
[ August 09, 2005: Message edited by: satya mamillapalli ]
satya mamillapalli
Ranch Hand

Joined: Jun 22, 2005
Posts: 63

[ August 10, 2005: Message edited by: satya mamillapalli ]
satya mamillapalli
Ranch Hand

Joined: Jun 22, 2005
Posts: 63
Jay Pawar
Ranch Hand

Joined: Aug 27, 2004
Posts: 411
Originally posted by satya mamillapalli:
Thanks Jon for your Venn Diagram concept..I understood Line 1 clearly now..

A ref2 = (A) ref1; <- ref1 type is B How can this be cast to type A.Should n't it throw compiler error.,
Please Advise..

Thanks
Satya.

[ August 09, 2005: Message edited by: satya mamillapalli ]


Satya,
Why do you think that the above line of code should throw compiler error ?
I am trying to understand your view point and may be I can help you in right direction. I have read all the comments posted by other users and they seem good to me.
Awaiting your comment...


Cheers,<br />Jay<br /> <br />(SCJP 1.4)<br />Heights of great men were not achieved in one day, they were toiling day and night while their companions slept.
satya mamillapalli
Ranch Hand

Joined: Jun 22, 2005
Posts: 63
I was asking how reference type B fits into type A even by explicit casting?

Shouldn't this be obvious and give compile error.. Please explain,,
Jay Pawar
Ranch Hand

Joined: Aug 27, 2004
Posts: 411
Originally posted by satya mamillapalli:
I was asking how reference type B fits into type A even by explicit casting?

Shouldn't this be obvious and give compile error.. Please explain,,




Line 1: this is ok because we are assigning child class object to superclass type. and I guess this is the obvious case you are talking about.

Line 2: why does compiler not issue an error on Line 2 , when you explicitly type-cast refB to A, the compiler sees an explicit cast which is in fact a safe type cast. However, as you stated correctly there is no need to do explicit casting in case of Line 2.

Compiler sees no issues in case of Line 1 and Line 2 code.

Let me know if I was able to clear your doubt....
[ August 10, 2005: Message edited by: Jay Pawar ]
Jon Egan
Ranch Hand

Joined: Mar 24, 2004
Posts: 83
Satya,

In case Jay's reply didn't settle this for you:



Line 2 works here because of the same principle that allows line 1 to work. To review Line 1, Since class C "IS-A" B, we can assign any C into a B reference. The same thing applies for line 2 - B "IS-A" A, so any time we have any B reference, we can "call it" an A (that is, cast it to A, or assign it to an A reference).

I think this can be less intuitive when our classes are things like A, B, and C. I find, when I'm having trouble following a rule by looking at it abstractly, it makes it easier to look at a more concrete example. Let's modify the example to use a common OO class hierarchy:



Then, you can see more easily:
  • All Ellipses are Shapes (Ellipse "IS-A" Shape).
  • All Circles are Ellipses (Circle "IS-A" Ellipse).
  • And, because of those first two points, we can also say All Circles are Shapes (Circle "IS-A" Shape)



  • Then, let's look back at the code we were trying to understand, rewritten in terms of the new classes:



    Here, on Line 1, we're making a Circle, but we're "calling it" (or, even better, "referring to it as") an Ellipse. Fair enough, we can "refer to it" as anything that it "IS" - which includes Circle, Ellipse, Shape, and (implicitly, always) Object.

    On line 2, we know that whatever is in ref1, it must be that it "IS" an Ellipse. The compiler can't know that it's actually a Circle, but it knows that the reference is of type Ellipse, so it at least knows that much. Well, anything that "IS" an Ellipse certainly also "IS" a Shape. So the cast is safe (and since we were able to say "certainly", it is even unnecessary). Same goes for assigning to the Shape variable.

    What WOULDN'T be safe is the following:



    Here, in Line 1, we should get a compiler error. Not all Shapes are Ellipses, and the compiler can tell about the type of the right-hand-side at compile time, so it can be certain this won't work.

    In Line 2, the class of the object in ref2 is a Shape, but the compiler can't be certain about that, so it has to let it slip without complaining. The type of the reference ref2 is Shape, though, and so the compiler at least requires you to insist that you know what you're doing, by doing the explicit cast. Once the cast is in place, the compiler is perfectly fine with the assignment step.... you have insisted that ref2 "IS-A" Ellipse, and if it takes your word there, the assignment is just fine.

    But: in step two, we're lying to the compiler when we make that cast... we'll pay for that at runtime, when we try to cast the Shape object referenced by ref2 to something that it "IS"-not, we get the ClassCastException.


    Hope this helps,
    -- Jon
    satya mamillapalli
    Ranch Hand

    Joined: Jun 22, 2005
    Posts: 63
    Thanks Jon , It makes total sense now..

    Satya
     
    I agree. Here's the link: http://aspose.com/file-tools
     
    subject: Ploymorphism code in this example? Please Explain.