6

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?

Laurel
  • 5,965
  • 14
  • 31
  • 57
  • Does this answer your question? [Overloaded method selection based on the parameter's real type](https://stackoverflow.com/questions/1572322/overloaded-method-selection-based-on-the-parameters-real-type) – Progman Feb 11 '21 at 17:23
  • @Progman not really, as I was interested in this specific example with interaction between different classes and the `this` reference. – MercuryHack Feb 11 '21 at 18:50

2 Answers2

4

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.
Thiyanesh
  • 2,360
  • 1
  • 4
  • 11
  • So what you are saying is that if the method is an inherited method (not overrided), than `this` is treated as an instance of the superclass? Because this is what I think is happening when `p.join(this)` is called in `lec3.addAttendant`, being lec3 an OnlineLecture. – MercuryHack Feb 11 '21 at 18:34
  • 2
    Resolution of `this` as a parameter entirely relies in the reference frame of the method and in this case, the resolved method at runtime is inside the `Lecture` class and hence it matches to a method implemented by `p`(or any of its subclass - polymorphism is used on `p`) that matches the method with `Lecture` signature. – Thiyanesh Feb 11 '21 at 18:39
  • Thank you very much. Does this means that the `join(OnlineLecture)` of `Person` and `Teacher` classes will never happen to be called because `OnlineLecture` inherit the `addAttendant` from `Lecture`, and `this` will always be a `Lecture`? – MercuryHack Feb 11 '21 at 18:46
  • Yes. If you need `join(OnlineLecture)` to be called, then override the `addAttendant` method on `OnlineLecture`. – Thiyanesh Feb 11 '21 at 18:50
  • Overriding the `addAttendant` method by simply re-writing it exactly as it is in `OnlineLecture` would lead to the different output? Because `this` at that point would be referring to `OnlineLecture`. This is my last question: am I on the right path? Thank you again. – MercuryHack Feb 11 '21 at 18:54
  • 1
    You are absolutely correct. (may be slight change in words, `this at that point would be referring to OnlineLecture` - `this will be statically linked to OnlineLecture when used as the parameter inside the OnlineLecture instance`). Again, your understanding is correct and English is not my first language. So i assume, we mean the same thing here. – Thiyanesh Feb 11 '21 at 18:58
1
  • 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
Md Kawser Habib
  • 1,966
  • 2
  • 10
  • 25
  • I actually didn't write the code I posted, I was asked to think about it and tell what the output would be. I still don't understand why `p.join(this)` when `this` is an OnlineLecture still take the `join` method with the Lecture parameter. – MercuryHack Feb 11 '21 at 18:31
  • I update my answer, try to explain this condition. Thanks for your nice query. – Md Kawser Habib Feb 11 '21 at 18:34