tags:

views:

880

answers:

8
+1  Q: 

Upcasting in java

In Java, suppose I have 3 classes, C extends from B which extends from A.

And I have one method:

public void f(A a);

If I do something like this:

C c = new C()
B b = (B) c;

f(b);

f accepts b as type C since C and B both extend from A. I wanted f to receive b as type B and not type C.

Is there anyway to force this upcasting?

+8  A: 

f() will always receive something typed as A (despite under the covers it's actually a B or C, and can be downcast appropriately).

You can define an additional f() thus

f(B b);

and if necessary

f(C c);

and the correct one will be called depending on the class of the argument. i.e. the compiler determines which function is called depending on the type of the argument. This is different from a dynamic dispatch (or polymorphism) which would occur at runtime.

Note that your cast in the question is redundant. You can write:

C c = new C()
B b = c;

f(b);

since C extends from B, C is a B.

Brian Agnew
+1  A: 

I wanted f to receive b as type B and not type C.

A C is a B is an A.

Inside of f, f only sees the A part of its parameter a If f calls a public A function that is overridden in C, the C override is called.

That's how virtual functions work. The idea being, the object referred to is really a C, so it should exhibit C behavior. If you want B behavior, pass a B instance, not a C instance.

If 'C' and "B' should have the same behavior, don't ovveride that behavior in C.

Why do you want to do this?

tpdi
This is exactly what I am trying to understand. I want the parameter to exhibit B behavior. I guess I have to create a new B instance from C before passing it to f. Thank you.
simao
If you want f to operate on a B, why not just create a B? Where does C come in?
tpdi
+1  A: 

Your question does not make sense. What do you mean by "f accepts b as type C"?

f accepts b as type A, since the method signature says "A". If you invoke methods on b, they will be invoked on C if C overrides them. But this is standard behaviour in Java (all methods are like virtual methods in C++), and there's no way to change it.

Maybe you can describe your actual problem, then we might be able to help.

sleske
I want f to call methods on the parameter depending on its type, so if the parameter is a C I want to call C.deliverTo(), but if the parameter is a B I want to call B.deliverTo(). Thank you.
simao
+2  A: 

You seem to be confused about the difference between compile time type and runtime type.

You are creating an object (pointed at by the references c and b) of type C, and it will stay a C, because it is impossible to change an object's runtime type and therefore behaviour; by casting you can merely change its compile time type, which affects how the compiler treats it.

Can you give some more information about the concrete problem you're trying to solve? Most likely there is a way to achieve your goal by changing the design.

Michael Borgwardt
Yes there is another way to do what I want, I can create new instance of B from a C, but I wanted to understand why I couldn't do this the way I tried first :) Thank you.
simao
A: 

The fact that you can pass any subclass of A as a parameter of f(A a) is inherent to OO in Java, there's no way you can go around this. If C extends A, you can always use it where an A is expected.

GClaramunt
A: 

you can use reflection to check if the parameter is of class A:

public void f(A a) {
  if (a.getClass() == A.class) {
    // ok, go on
  }
  else {
    System.err.println("The parameter is not of class A!");
  }
}

don't know if that's what you want, but it may be helpful.

cd1
A: 

Your issue can be resolved by understanding the difference between REFERENCE type and INSTANCE type. When you cast you C object as a B, you only change the reference type - the actual instance of the object is still a C object - this should be quite obvious if you look at your object in debug mode. Changing the reference type only impacts how the compiler handles/perceives the code - runtime behaviour should not be affected. Any method call on an C object will always invoke C's method (regardless of whether or not the object has been casted to something else). If you overode a method from B and want to invoke B's version of the method, then you will need to create a B instance.

A: 

The fact your code has a reference to an A doesn't mean the object being pointed too is an A. If it's a a C it remains a C. The reference in your source only limits the available methods in your source. Casting is only necessary because sometimes one needs a method on a different type and we need to trick the compiler so it let's us start using methods on the castes to type. Naturally the cast can fail at runtime if the attempt us invalid.

mP