views:

1659

answers:

7

Hello,

I have something unclear concerning casting reference variable in Java.

I have two classes A and B. A is the super class of B. If I have the two objects, and then the print statement:

A a = new A(); //superclass

B b = new B(); //subclass

System.out.println ((A)b);

then what exactly is happening when the println method is executed?

I know that because B is a subclass of A, I am allowed to make the following cast:

A a2 = (A)b;

I also know that when println takes a reference variable as argument, then the toString() method of the class, which has created the object-argument, is invoked (implicitly). This is so, because the method println() is looking for an argument of type String, and the toString() method represent the object as a string. And even if we don't write toString(), the method is invoked - implicitly. So, the following two statements are equivalent:

System.out.println (b);

System.out.println (b.toString());

So, my question is: what is the implicit action taken when we have

System.out.println ((A)b);

?

I suppose that the type of the reference variable b is automatically changed from B to A. The variable should still be pointing to the same object - the one created with

B b = new B();

but just the type of b would be now changed. Is this correct? Another question: even though I have changed the type of b to the type of the superclass, are the overriden methods in the subclass going to be called, and not those of the superclass?

Thanks a lot.

Regards

+1  A: 

Is this correct?

Sort of. The result of the casting expression would be of the A type. The type of the 'b' variable will always remain of type B.

Another question: even though I have changed the type of b to the type of the superclass, are the overriden methods in the subclass going to be called, and not those of the superclass?

The instance methods of the underlying object will be called. Example:

class Foo {
    public static void main(String[] args) {
        B b = new B();
        assert "B".equals(((A) b).m());
    }
}

class A {
    String m() { return "A"; }
}

class B extends A {
    String m() { return "B"; }
}
yawmark
+1  A: 

Since Java methods all have dynamic dispatch, which function gets called doesn't depend on the static type of the reference. Therefore, the results will be the same with or without the cast. [The results could be different if you were downcasting - the casting version could throw an exception]

jpalecek
+4  A: 

The cast has no impact in this case.

The System.out.println(XXX) takes parameters of different types (multiple overloaded versions) but in this case you would get the version that takes Object. Since every object in Java supports toString(), toString is invoked on the actual argument, no matter what it is.

Now, since all methods in Java are dispatched dynamically, the version that runs is the version that corresponds to the dynamic type. Casting an object of B to A only changes the static (declared) type of the expression. The dynamic type (what's really in there) is still a B. Therefore, the version in B gets invoked.

Uri
toString() is not invoked if the argument passed is String. In other cases yes.
Adeel Ansari
"The System.out.println(XXX) takes a parameter of type string" .... .this statement is not correct. You might need to rephrase it to make it correct.
Adeel Ansari
As Vinegar says, System.out.println(XXX) in this case takes a parameter of type Object, which it then internally calls toString on, before passing the result to the println(String) method.
Mike Houston
You guys are right, my bad. There's a version that takes String and a version that takes Object, and I thought (though I'm probably wrong) that the Object version is a convenience method for the one that takes String.
Uri
All methods are NOT dispatched dynamically. Overloads are chosen at compile time. If you have foo(String) and foo(Object), and your reference is of type Object, the latter will be called, even if the actual type is String.
Outlaw Programmer
But the implementation of println eventually calls toString(), and that is dispatched dynamically.
Uri
Yes, in this particular case. In other cases, overloaded methods could do wildly different things.
Outlaw Programmer
I still don't understand your concern. Obviously the static overloaded version is determined first. But when it comes to making the call (in this case to toString), the dispatch is dynamic unless the class is final and the compiler can determine the target at compile time.
Uri
+1  A: 

Always think of your object as the type it's instantiated as (B in your case). If it's upcast to A think of it as--hmm--think of it as B putting on A clothes. It may look like an A, and you may not be able to do any of the nice B things you want to do, but inside the clothes it's still a B--the clothes don't change the underlying object at all.

So the summary would be--you can only call the methods in A, but when you call it, it goes straight through and executes it as it would if it was a B.

Bill K
+2  A: 

There are many declarations of println(...) in the PrintStream class (which is the type of System.out).

Two of them are:

void println(String x)
void println(Object x)

When you call println((A)b) the compiler chooses to call println(Object) because A is not String (or any of the other types that println supports). When you call println(b.toString()), the compiler chooses println(String) because you are passing a String.

In your case, casting b to A has no effect since println() doesn't have a declaration for either A or B types. But the cast will still occur (because you asked for it), or maybe it won't because the compiler optimises it away as it knows it is redundant and it can't fail and has no effect.

It is not idiomatic to write:

A a2 = (A)b;

as this is redundant since B is a subclass of A. It may be that the compiler will optimise away the cast (which is a run-time operation to check whether an object is of a particular type, never to change it's type).

Once an object of type B is constructed, it's type never changes. It is always a B:

class B extends/implements A {...}
B b = new B();   // construct a B
A a = b;         // assign a B to an A variable, it's superclass
A a = (A) b      // as above including check to see that b is an A (redundant, may be optimised away).

B b = a;         // Syntax error, won't compile
B b = (B) a      // Will check whether a is of type B then assign to variable b

In the last case, since B is a subclass of A, it may be that a holds an instance of B and the cast will succeed. Or it may be that a holds an instance of some other class that extends/implements/is A and isn't a B and you'll get a ClassCastException.

So since an object of type B always retains it's identity (it's "B"-ness) then any (instance-) methods called on that object will always call B's implementation regardless of whether the variable through which you access the object was declared as A or B.

Remember, you can only call methods that are declared in the class that the variable is defined as.

So for example, if B declares a method b_only() then the compiler won't allow you to write a.b_only(); you could write ((B)a).b_only() though.

Adrian Pronk
A: 

The casting, as has been mentioned, is irrelevant in this case due to overridden methods being dynamically bound. Since the toString is present in all objects it meets this condition and thus the object type and method to call are determined at runtime.

Please note though, this is NOT the case with all methods since only overridden methods are dynamically bound. Overloaded methods are statically bound. Many of the answers here mention that java methods are always dynamically bound, which is incorrect.

See this question for a more detailed explanation.

Robin
A: 

i wanna ask about usin toString method :: if i wanna pass it to the Println ::system.out .println (toString) what do i have to define in the class of tostring method ???? what does it have to return ?!?!?!?!?**