3

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

user42155
  • 48,965
  • 27
  • 59
  • 60

8 Answers8

4

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
  • 88,451
  • 51
  • 221
  • 321
  • toString() is not invoked if the argument passed is String. In other cases yes. – Adeel Ansari Jan 31 '09 at 04:37
  • "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 Jan 31 '09 at 04:38
  • 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. – Kothar Jan 31 '09 at 12:17
  • 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 Jan 31 '09 at 17:12
  • 1
    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. – Tim Frey Feb 02 '09 at 14:48
  • But the implementation of println eventually calls toString(), and that is dispatched dynamically. – Uri Feb 02 '09 at 15:39
  • Yes, in this particular case. In other cases, overloaded methods could do wildly different things. – Tim Frey Feb 02 '09 at 16:30
  • 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 Feb 02 '09 at 17:59
3

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
  • 13,486
  • 7
  • 36
  • 60
1

I think when we use reference variable in java and by using this variable we can assign a object of any class type. most of the cases we create a reference variable of Interface and abstract class because we can't create the object of interface and abstract class so assign the object of class in reference variable of Interface or abstract class. Ex-

Interface X {
 public abstract void xx();
 public abstract void yy();
}

Class XXX implements X {
   ...........
}

Class XY extends XXX {
  X xy = new XXX();
} 

here xy is a reference of Interface X and assign the object of Class XXX in the reference of Interface.

so according to my point of view by using reference variable we can also use interface to participate in Object creation.

Leo
  • 37,640
  • 8
  • 75
  • 100
1

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,934
  • 14
  • 16
1

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
  • 47,058
  • 7
  • 102
  • 144
1

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
  • 62,186
  • 18
  • 105
  • 157
0

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?

in this case the method of subclass b is called ; to convincingly understand why; you may relate to the following real world scenario

consider a parent class Father exhibiting a behaviour(method): height defined as

the father is tall ;height = 6'2"

Son is a child class inheriting the height behavior from Father ;as a result he is also tall; height being 6' clearly overriding the behaviour

whenever your subclass Son calls the behavior height on his name he displays the overridden behavior i.e his own height 6' .

Community
  • 1
  • 1
da7
  • 109
  • 1
  • 8
0

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.

Community
  • 1
  • 1
Robin
  • 24,062
  • 5
  • 49
  • 58