aspose file tools*
The moose likes Programmer Certification (SCJP/OCPJP) and the fly likes Polymorphic methods, shadowed variables 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 "Polymorphic methods, shadowed variables" Watch "Polymorphic methods, shadowed variables" New topic
Author

Polymorphic methods, shadowed variables

Caroline Bogart
Greenhorn

Joined: Nov 21, 2001
Posts: 24
Hi everyone,
I realize this topic has been properly addressed, but I find that I have apparently learned this backwards and would appreciate some live human straightening out what I've just got all twisted up.
This post's question boils down to two things:
(1) Is the left operand the reference and the right operand the class?
(2) Which determines which method is invoked, and which determines which variable is accessed?
We know that Khalid says (p. 181): "When a method is invoked on an object using a reference, it is the class of the current object denoted by the reference, not the type of the reference that determines which method implementation will be executed."
So, for example:
superRef = subClassObject;
The class of the current reference: Super
The type of the reference: Sub
Or not?
I learned this backwards and desperately need straightening out.
The following code is adapted from Khalid p. 183, and clearly shows that the left hand operand always determines the method invoked.
Or not. I don't know. I seem to have curled my brain into a ball.
I thank you all for your replies.
-- Caroline
<CODE>
interface IStack {
void push();
void pop();
}
interface ISafeStack extends IStack {
void isEmpty();
void isFull();
}
class StackImpl implements IStack {
public void push() {}
public void pop() {}
public void peek() {}
}

class SafeStackImpl extends StackImpl implements ISafeStack {
public void isEmpty() {}
public void isFull() {}
}
class StackUser {
public static void main(String args[]) {
new StackUser();
}
public StackUser() {
SafeStackImpl safeStackRef = new SafeStackImpl();
separator();
debug("SafeStackImpl safeStackRef.getClass: " + safeStackRef.getClass());
safeStackRef.push();
safeStackRef.pop();
safeStackRef.peek();
safeStackRef.isEmpty();
safeStackRef.isFull();
debug("safeStackRef CAN access push, pop, peek");
debug("safeStackRef CAN access isEmpty, isFull");
separator();
StackImpl stackRef = safeStackRef;
debug("StackImpl stackRef.getClass: " + stackRef.getClass());
stackRef.push();
stackRef.pop();
stackRef.peek();
//stackRef.isEmpty();
//stackRef.isFull();
debug("stackRef CAN access push, pop, peek");
debug("stackRef CANNOT access isEmpty, isFull");
separator();
ISafeStack isafeStackRef = safeStackRef;
debug("ISafeStack isafeStackRef.getClass: " + isafeStackRef.getClass());
isafeStackRef.push();
isafeStackRef.pop();
//isafeStackRef.peek();
isafeStackRef.isEmpty();
isafeStackRef.isFull();
debug("isafeStackRef CAN access push, pop");
debug("isafeStackRef CANNOT acccess peek");
debug("isafeStackRef CAN access isEmpty, isFull");

separator();
IStack istackRef = safeStackRef;
debug("IStack istackRef.getClass: " + istackRef.getClass());
istackRef.push();
istackRef.pop();
//istackRef.peek();
//istackRef.isEmpty();
//istackRef.isFull();
debug("istackRef CAN access IStack push, pop");
debug("istackRef CANNOT access peek");
debug("istackRef CANNOT access isEmpty, isFull");
separator();
Object objRef = safeStackRef;
debug("Object objRef.getclass: " + objRef.getClass());
//objRef.push();
//objRef.pop();
//objRef.peek();
//objRef.isEmpty();
//objRef.isFull();
debug("objRef cannot access anything except Object methods");
StackImpl stackImpl = new StackImpl();// superclass reference
SafeStackImpl safeStackImpl = new SafeStackImpl();// subclass reference
// stackImpl: push, pop, peek
// SafeStackImpl: isFull, isEmpty
// REFERENCE determines method
stackImpl = safeStackImpl;// upcasting
stackImpl.push();
stackImpl.pop();
stackImpl.peek();
//stackIimpl.isEmpty();
//stackImpl.isFull();
// REFERENCE determines method
safeStackImpl = (SafeStackImpl) stackImpl;
safeStackImpl.push();
safeStackImpl.pop();
safeStackImpl.peek();
safeStackImpl.isEmpty();
safeStackImpl.isFull();

}
private void debug(Object arg) { System.out.println(arg.toString()); }
private void separator() { System.out.println("----------------"); }
}
</CODE>



------------------
I love JavaRanch!


I love JavaRanch!
Uma Viswanathan
Ranch Hand

Joined: Jun 14, 2001
Posts: 126
Let us take the simple example...
interface IStack {
void push();
void pop();
}
class StackImpl implements IStack {
public void push() {System.out.println("push");}
public void pop() {}
public void peek() {}
}
class StackUser1 {
public static void main(String args[]) {
new StackUser1();
}
public StackUser1() {
IStack istackRef = new StackImpl(); //according to //conversion rule, this is valid
istackRef.push(); //compiler knows that the type of //istackRef is only IStack and checks whether push //is in IStack. Exists and so, no problem
istackRef.peek(); //peek does not exist is in IStack. So, //compile time error
}
}
Read the explanation in the comment (i.e, after //)
If you override the method, then only at run time, we need to consider the run time type of the object.
Hope this helps...
Uma
Caroline Bogart
Greenhorn

Joined: Nov 21, 2001
Posts: 24
Actually, no, though I appreciate the effort.
I just want to define some terms.
After this quote, I will ask to define terms from the quote. That is my actual question:
Khalid, p 181

When a method is invoked on an object using a reference, it is the class of the current object denoted by the reference, not the type of the reference, that determines which method implementation will be executed.

What is the definition of:
1. reference
2. class of the current object
3. type of the reference
Thanks again,
Caroline

Originally posted by Uma Viswanathan:
Let us take the simple example...
interface IStack {
void push();
void pop();
}
class StackImpl implements IStack {
public void push() {System.out.println("push");}
public void pop() {}
public void peek() {}
}
class StackUser1 {
public static void main(String args[]) {
new StackUser1();
}
public StackUser1() {
IStack istackRef = new StackImpl(); //according to //conversion rule, this is valid
istackRef.push(); //compiler knows that the type of //istackRef is only IStack and checks whether push //is in IStack. Exists and so, no problem
istackRef.peek(); //peek does not exist is in IStack. So, //compile time error
}
}
Read the explanation in the comment (i.e, after //)
If you override the method, then only at run time, we need to consider the run time type of the object.
Hope this helps...
Uma


------------------
I love JavaRanch!
Uma Viswanathan
Ranch Hand

Joined: Jun 14, 2001
Posts: 126
OK. Let me try to explain you...
class base
{
void display(){}
}
base b = new base();
Now, b is called the reference variable and the new operator creates the object at run time in the heap.
We will split the statement base b = new base(); into two parts
I part: base b
II part: new base();
We have to find out the type of the reference from the I part
i.e, from
base b
It says that b is of type base. i.e., type of the reference is base. OK.
We have to find out the class of the current object from II part
new base();
i.e, class of the current object is base, in this case
The reference variable b points to the newly created object.
If we invoke b.display();
then it means that we are calling the method display() on the newly created object (which is in memory) using the reference variable b.
Now, is the part of the sentence you had quoted "When a method is invoked on an object using a reference" clear?
Now let us consider two classes...
class base{
void display()
{ System.out.println( "base type" );
}
}
class derived extends base{
void display()
{ System.out.println( "derived type" );
}
}
Now base b = new base();
Here the type of the reference is base and the class of the current object is of type base.
If we invoke b.display(), at run time, it will execute the method which is in the class of the current object. i.e, it will execute the method display() in the class base
Now base b = new derived();
Here the type of the reference is base and the class of the current object is derived.
If we invoke b.display(), at run time, it will execute the method which is in the class of the current object. i.e, it will execute the method display() in the class derived
Now, we have seen that the method invocation b.display() executes the method display() in the class base if the class of the current object is base. Otherwise, it executes the method display() in the class derived if the class of the current object is derived.
So, at run time, the class of the current object determines which method implementation will be executed.
i.e.,At run time, the type of the reference does not determine which method implementation will be executed.
Let us reframe your quote so that you can easily understand...
"When a method is invoked on an object using a reference, the class of the current object determines which method implementation will be executed. The type of the reference does not determine which method implementation will be executed."
Hope this helps...
Velentin and Jose: Your comments are welcome for this question and also for answer. Also both of you could add valuable points...
Uma



Caroline Bogart
Greenhorn

Joined: Nov 21, 2001
Posts: 24
Uma,
I thank you for your patience, but you will now see why I remain confused.
In my original example, it is the type of the reference, not the type of the object that is determining method invocation.
In that example there are 5 references. Each reference is assigned the object of the type SafeStackImpl.
The reference types are:
SafeStackImpl
StackImpl
ISafeStack
IStack
Object
When a reference of type SafeStackImpl receives an object of type SafeStackImpl, the invokable methods run all the way up the heirarchy. This reference can invoke push, pop, peek, isEmpty and isFull.
When a reference of type StackImpl receives an object of type SafeStackImpl, the invokable methods are: push, pop, peek.
When a reference of type ISafeStack receives an object of type SafeStackImpl, the invokable methods are: push, pop, isEmpty, isFull.
When a reference of type IStack receives an object of type SafeStackImpl, the invokable methods are: push, pop.
And, finally, when a reference of type Object receives an object of type SafeStackImpl, the invokable methods are only those known to the Object class (such as getClass()).
Every time the object class on the right side of the assignment reduces accessibility to the inheritance tree, the pool of invokable methods is reduced. What's changing is the class of the object, not the type of the reference.
<PRE>
_______________ _______________
|<<interface>>| | Object |
| IStack | |_____________|
|_____________| |_____________|
|_____________| | |
| push( | | getClass() |
| pop() | |_____________|
|_____________| ^
^ ^ |
| |_ _ _ implements _ extends
extends | |
| | |
_______________ ________________
|<<interface>>| | StackImpl |
|ISafeStack | |______________|
|_____________| |______________|
|_____________| | push() |
| isFull() | | pop() |
| isEmpty() | | peek() |
|_____________| |______________|
^ ^
| |
- - - - - - - ______|
| |
________________
| SafeStackImpl |
|_______________|
|_______________|
| |
| isFull() |
| isEmpty() |
|_______________|
</pre>
A reference of type SafeStackImpl has access to the entire tree's worth of methods because, it appears, it is assign an object of type SafeStackImpl.
A reference of type SafeStackImpl has access only to the methods of the type Object, if it is assign an object of the class Object.
The right hand side, the class of the object, not the reference type, is determine what is accessible.
Thank you again in helping me with this confusion,
Caroline

[This message has been edited by Caroline Bogart (edited November 30, 2001).]
[This message has been edited by Marilyn deQueiroz (edited November 30, 2001).]
Jose Botella
Ranch Hand

Joined: Jul 03, 2001
Posts: 2120

When a method is invoked on an object using a reference, it is the class of the current object denoted by the reference, not the type of the reference, that determines which method implementation will be executed.
What is the definition of:
1. reference
2. class of the current object
3. type of the reference
Thanks again,


TypeOfTheReference reference =
new ClassOfTheCurrentObject();
reference.methodDeclaredInTypeOfTheReference();
JLS says that variables has types but objects has classes. Though type really is accepted as synonymous for class.


SCJP2. Please Indent your code using UBB Code
Jane Griscti
Ranch Hand

Joined: Aug 30, 2000
Posts: 3141
Hi Caroline,
Determining which 'type' will be used is not as simple as saying 'it will be the left hand operand or the right hand operand'. You need to consider what happens at compile time and what happens at runtime.
At compile time, the declared type of the reference rules.
At runtime, the actual type (runtime class) of the object rules (except when accessing fields, in which case the declared type rules).
In your example,
safeStackRef has a declared and actual type of SafeStackImpl
<code>
SafeStackImpl safeStackRef = new SafeStackImpl();
</code>
The SafeStackImpl type inherits the methods <code>push(), pop()</code> and <code>peek()</code> from StackImpl and implements the methods <code>isEmpty()</code> and <code>isFull()</code>.
Any object of type SafeStackImpl will have all the defined behaviours, both at compile time and runtime.
Now, when you create stackRef, you are declaring it as type StackImpl and assigning it a SafeStackImpl object.
A StackImpl type only contains behaviours for push(), pop() and peek(). The assignment will work as a SafeStackImpl object also contains these methods; however, if you try to invoke the SafeStackImpl methods isEmpty() or isFull() using the StackImpl reference you will get a compile error. The compiler recognizes that these methods are not part of the declared types (StackImpl) behaviour.
Next you assign the safeStackRef object to an ISafeStack reference. Again this will work because the safeStackRef object has all the behaviours declared in ISafeStack: push(), pop(), isEmpty() and isFull(). BUT when you try to invoke peek() the compiler raises an error; an ISafeStack object does not have peek() behaviour.
The same thing occurs when you assign the safeStackRef object to an IStack or Object reference.

A method invocation will only work at compile time if the method is part of the referencing variables declared type

Looking at this in terms of Khalid's comments:
When a method is invoked on an object using a reference, it is the class of the current object denoted by the reference, not the type of the reference that determines which method implementation will be executed.
<code>
StackImpl stackRef = safeStackRef; // whose type is SafeStackImpl
safeStackRef.isEmpty(); // produces compile error
</code>
StackImpl is the type (class) of the stackRef reference variable. It is the class of the current object denoted by the reference.
SafeStackImpl is the type (class) of the safeStackRef object being referenced.
Even though we know that the object safeStackRef will have the behaviour isEmpty() the method is not part of the contract for a StackImpl type and therefore fails at compile time.
Khalid's wording is a little awkward but I think that is the point they are trying to make.
Hope that helps.


Jane Griscti
SCJP, Co-author Mike Meyers' Java 2 Certification Passport
Caroline Bogart
Greenhorn

Joined: Nov 21, 2001
Posts: 24
My friends, I feel I am close to getting this but that words are getting in my way.
Jose wrote:
<quote>

TypeOfTheReference reference =
new ClassOfTheCurrentObject();
reference.methodDeclaredInTypeOfTheReference();
</quote>
Khalid (p.181) says:
<quote>

When a method is invoked on an object using a reference, it is the class of the current object denoted by the reference, not the type of the reference that determines which method implementation will be executed.
</quote> (emphasis added)

Which part below, then, is wrong?
The "current object" is the operand being assigned to a reference
The "reference" is the operand receiving the assignment.
The "class of the current object denoted by the reference" is:
(a) the current object's class (being assigned)
ObjType objType = new SubType()
(b) the current reference's class (receiving assignment)
ObjType objType = new SubType()

Because I know that assigning a subclass to a superclass reference reduces that references ability to "see" superclass behavior, the answer must be that the "class of the current object denoted by the reference" is the right hand operand.
My problem is probably with the word "denoted." When something is "denoted by the reference," the reference rules the day.
Excuse me for being so dense, and thanks again,
Caroline

------------------
I love JavaRanch!

[This message has been edited by Caroline Bogart (edited December 02, 2001).]
Mathew Kuruvilla
Ranch Hand

Joined: Nov 27, 2001
Posts: 135
"When a method is invoked on an object using a reference, it is the class of the current object denoted by the reference, not the type of the reference that determines which method implementation will be executed."
Maybe this is a little better restated as follows:
"When a method is invoked on an object using a reference AT RUN TIME, it is the class of the current object denoted by the reference, not the type of the reference that determines which method implementation will be executed."
The specific COMPILATION error given by the first example is discussed in page 177 of Khalid's book. The COMPILER has no way of knowing what object the REFERENCE 'stackRef' is DENOTING, at COMPILE TIME.
In the first example given within this topic, there was no overridden method in the superclass. Khalid is talking within the context of shadowed variables and overridden methods for a program running within the JAVA VIRTUAL MACHINE, when it is possible to determine exactly which object is DENOTED by any given REFERENCE.
The example also clouds the issue by having multiple inheritance using Interfaces. In this case, the isEmpty and isFull methods could not ever be present in the superclass because it is part of the ISafeStack Interface, which is not related to the reference type at all, due to the multiple inheritance stuff.
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Polymorphic methods, shadowed variables