Home > Mobile >  Why does line 4 only print out "B move"?
Why does line 4 only print out "B move"?

Time:11-21

I'm trying to understand this code's outputs. I understand the other outputs except for line 4's.

The code:

class A{
    public void move(Object o){
        System.out.println("A move");
    }
    public void keep(String s){
        System.out.println("A keep");
    }
}
class B extends A{
    public void move(Object o){
        System.out.println("B move");
    }
    public void keep(Object o){
        System.out.println("B keep");
    }
}

class C extends B{
    public void move(String s){
        super.move(s);
        System.out.println("C move");
    }
    public void keep(String s){
        super.keep(s);
        System.out.println("C keep");
    }
}

public class main {
    public static void main(String[] args) {
        A a = new A();
        A b = new B();
        A c = new C();

        a.move("Test"); //line1

        b.move("Test"); //line2

        b.keep("Test"); //line3

        c.move("Test"); //line4

        c.keep("Test"); //line5
    }
}

The output:

A move

B move

A keep

B move

A keep
C keep

I was expecting line 4 to print out "B move. C move" but instead it just ended at "B move". I thought that it would have the same logic as line 5. Anyone knows what's going on?

CodePudding user response:

Overloads are determined by the compiler, overrides are done at runtime. A method's "identity" includes its parameter types and its return type as well as its name. Therefore:

  • B.keep does not override A.keep, but B.move does override A.move.
  • C.move does not override B.move/A.move. C.keep overrides A.keep but not B.keep.

Hence, The compiler sees, at line 4, c.move("Test"), and determines that this is a call on an expression of type A (because you declared variable c as A c;. Looking just at what A has to offer, there's only one method named move, whose signature is void move(Object). So that gets encoded in the class file.: A call to A.move(Object).

Then at runtime, the runtime checks for the most specific implementation of the void move(Object) method on the actual type of the object that the c variable is pointing at, which is in fact an object of type C. The most specific implementation of move(Object) is found in B: B has a void move(Object) implementation. C has a void move(String), but this doesn't "count" - it does not have the same identity, effectively, it has a different name and has nothing whatsoever to do with A's move(Object) - that they have the same actual method name (move) is a coincidence.

Two tips:

  • Always use the @Override annotation when you intend to override. Had you done so here, the compiler would have told you that e.g. B's keep(Object o) method is not an override, and it would have also flagged C's move method as erroneous, had you flagged it as @Override. (This annotation does not mean 'this method is an override'. It means: If this method doesn't override or implement anything, generate a compile time error. A method whose full signature matches a method in a parent type still overrides it, even without the annotation. It's just compiler-checked documentation, which is a good thing you should use).
  • In general do not write the same method name with different variable types if those variable types have a relationship. It results in confusion like this.
  • Related