In Java, what does 'this' indicate in a superclass method called on a subclass that inherits that method?

Viewed 118

The code is this:

class Main {  
  public static void main(String args[]) { 
        Person p1 = new Student();
        Person p3 = new Teacher();
        Student p4 = new Student();
        OnlineLecture lec3 = new OnlineLecture();
        
        lec3.addAttendant(p1);
        lec3.addAttendant(p3);
        lec3.addAttendant(p4);
  }
}


abstract class Person {
    public void join(Lecture lec) { 
        System.out.println("Joining "+lec);
    }
    public void join(OnlineLecture lec) {
        System.out.println("Joining "+lec);
    }
}

class Student extends Person {
    public void join(Lecture lec) {
        System.out.println("Student joining "+lec);
    }
}

class Teacher extends Person {
    public void join(OnlineLecture lec) {
        System.out.println("Teacher joining "+lec);
    }
}
    
class Lecture {
    public void addAttendant(Person p) {
        p.join(this);
    }
    public String toString() {
        return "a lecture";
    }
}

class OnlineLecture extends Lecture {
    public String toString() {
        return "an online lecture";
    }
}

I don't understand why the output I get is this:

Student joining an online lecture
Joining an online lecture
Student joining an online lecture

Shouldn't 'join(this)' in the 'addAttendant' method called on lec3 result in a 'join(OnlineLecture lec3)', and therefore give this

Joining an online lecture
Teacher joining an online lecture
Joining an online lecture

as output?

2 Answers

Polymorphism & Overloading

Polymorphism

  1. Polymorphism is exhibited when a reference.method() is invoked
  2. This is by nature a dynamic behavior based on the actual object type referenced by the reference
  3. This is where the lookup tables(like vmt in c++) comes into play
  4. Depending on the object pointed by the reference, runtime will decide on the actual method to invoke

Overloading

  1. Method overloading in a compile time decision
  2. The signature of the method is fixed at compile time
  3. There is no runtime lookup needed for any polymorphism exhibited based on the method's parameter types
  4. The parameter is just a parameter for the method in context and it does not care about the polymorphism exhibited by the type

What is happening in the current example?

    static class Lecture {
        public void addAttendant(Person p) {
            p.join(this);
        }
    }
  1. Assuming there is a child class of Lecture overriding addAttendant, then polymorphism can control which method will be called based on the object type when someone invokes a method on a reference type of Lecture or one of its subclass(es).
  2. But, for any call that will eventually land on the Lecture.addAttendant, the method signature that matches the p.join(this) is join(Lecture)(even though p could be dynamically referenced). Here there is no polymorphism even though the object referenced by this could be a polymorphic type.
  • Inside addAttendant method, this inside p.join(this) is hold Lecture class, as there is no implementation of addAttendant in child class. So, it invoke join(Lecture lec) method. It ignores OnlineLecture behaviour.
class Lecture {
    public void addAttendant(Person p) {
        p.join(this);
    }
}

One approach to solving this ambiguous behavior, implement @Override the method addAttendant(Person p) inside OnlineLecture subclass :

public class InheritanceProblem {

    public static void main(String args[]) {
        Person p1 = new Student();
        Person p3 = new Teacher();
        Student p4 = new Student();
        OnlineLecture lec3 = new OnlineLecture();

        lec3.addAttendant(p1);
        lec3.addAttendant(p3);
        lec3.addAttendant(p4);
    }
}

abstract class Person {
    public void join(Lecture lec) {
        System.out.println("Joining " + lec);
    }

    public void join(OnlineLecture lec) {
        System.out.println("Joining " + lec);
    }
}

class Student extends Person {
    public void join(Lecture lec) {
        System.out.println("Student joining " + lec);
    }
}

class Teacher extends Person {
    public void join(OnlineLecture lec) {
        System.out.println("Teacher joining " + lec);
    }
}

class Lecture {
    public void addAttendant(Person p) {
        p.join(this);
    }

    public String toString() {
        return "a lecture";
    }
}

class OnlineLecture extends Lecture {
    @Override
    public void addAttendant(Person p) {
        p.join(this);
    }

    public String toString() {
        return "an online lecture";
    }
}

Output:

Joining an online lecture
Teacher joining an online lecture
Joining an online lecture
Related