My included code's output is pretty ugly, but it's just a code for understanding how different things may work in Java. The questioned line is marked with comments on the bottom half of the code.
class CsTorta extends Torta{
public CsTorta retegez(CsTorta a){
....
}
public CsTorta retegez(Torta a){
System.out.println("This method"); //<-----it calls this one and not the one above
....
}
}
public class NewClass {
public static void main(String[] args) {
Torta tt=new Torta(5);
Torta tcs=new CsTorta(3);
CsTorta cs=new CsTorta(4);
System.out.println("");
System.out.println(tcs.retegez(tcs)); //The line in question uses the cstorta retegez method (marked with "This method")
}
}
While the tcs's type in coding-time is the reference type, in runtime when i call the tcs.retegez method it recognizes its a cstorta type, but the parameter which is the same tcs remains the reference type (thats why it uses the cstorta marked method).
My question is: Is my conclusion correct: that the program only checks the "real" type of the object if it calls a method, and uses the reference type if it does not?
That's pretty much correct. What is needed here is understanding the difference between overloading and overriding.
Overriding occurs when you have a class that declares an instance method, and a subclass that declares the same method (same name, same parameters--the result type is usually the same but could be a subclass). There are multiple methods to choose from, but the exact method is determined at run time.
public class A {
public void method1(String s1, int s2) { ... }
}
public class B extends A {
@Override
public void method1(String s1, int s2) { ... }
}
A object = new B();
object.method1("xxx",2);
The decision about which method1
is run isn't made until run time. object
's real type is B
, so the method1
declared in B
is called.
Overloading is when two methods with the same name, but different parameters, are both present. By different parameters, I mean that the number of parameters is different, or the number of parameters is the same but the types are different. That is, they have different signatures. In that case, the decision on which method to call is made at compile time. (You can have a case where both overriding and overloading occur. The decision about which parameter signature to choose is made at compile time; but if there are multiple overriding methods with the same signature, the choice between those methods is made at run time.)
The key thing to remember is that if the decision is made at compile time, the compiler will not know what the "real" type of the object is. All it knows is how you've declared it. Thus:
public CsTorta retegez(CsTorta a){ // Number 1
....
}
public CsTorta retegez(Torta a){ // Number 2
System.out.println("This method"); //<-----it calls this one and not the one above
....
}
These are overloaded methods.
Your code looks like:
Torta tcs = // the compiler doesn't care
System.out.println(tcs.retegez(tcs));
The compiler has to decide whether to call Number 1 or Number 2. All the compiler knows is that the parameter is a Torta
. The actual value could be an object of Torta
, CsTorta
, or any other class. Or it could be null
(all right, you can't call tcs.retegez
if it's null, but if you said tcs.retegez(tcs2)
, then tcs2
could be null.) The compiler doesn't know, and doesn't care. All it knows is that it was declared as a Torta
, so it chooses the overloaded method with the Torta
parameter.
(To clarify further: the compiler will choose the deepest subclass it can. Example:)
class AnotherTorta extends Torta { ... }
class YetAnotherTorta extends CsTorta { ... }
AnotherTorta t3 = // whatever
YetAnotherTorta t4 = // whatever
tcs.retegez(t3);
// since AnotherTorta can't be cast to CsTorta, it chooses the Torta parameter
tcs.retegez(t4);
// here, t4 can be cast to either a Torta or CsTorta parameter, so it chooses the subclass, CsTorta