Let's also examine a situation where a METHOD is OVERRIDDEN in a subclass. In the class "Lorry", the method getCapacity has been IMPLEMENTED AGAIN: class Truck { private int capacity; public Truck(int capacity) { this.capacity = capacity; } public int getCapacity() { return this.capacity; } } class Lorry extends Truck { private int trailerCapacity; public Lorry(int capacity, int trailerCapacity) { super(capacity); this.trailerCapacity = trailerCapacity; } // HERE @Override public int getCapacity() { return super.getCapacity() + trailerCapacity; } } Let's create a new object of type "Lorry", and save the reference to a "Truck"-type variable. The type of the variable defines the operations that can be used: thus, we can only call methods defined in the "Truck" class. So let's call the "getCapacity" method, which is defined in the "Truck" class and overridden in the "Lorry" class. public class LorryTest { public static void main(String[] args) { //INITIALISE 'Lorry' object as a 'Truck' / superclass Truck vehicle = new Lorry(10, 20); System.out.println("Capacity: " + vehicle.getCapacity()); } } The program outputs Capacity: 30 From this example, we notice that the method call is directed to the implementation given in the Lorry class, even though the type of the variable is Truck. As a general rule, - The VARIABLE TYPE DETERMINES the AVAILABLE OPERATIONS / METHODS - The OBJECT TYPE determines the IMPLEMENTATIONS used by the METHODS This is called dynamic binding: Java always looks for the LATEST IMPLEMENTATION of each METHOD. https://docs [dot] google [dot] com/presentation/d/e/2PACX-1vTpe_7VNECaErEVyCbdD2Ud_CcEaS7sDoG7YD5ic--v34mX0QCzEZRQ-q-ENqkjTI6wiGT-Kh0rHNPl/embed?start=false&loop=false&slide=id.p ========================================== Note that dynamic binding works in the same way also with internal calls of an object. Let's look at a situation where the class 'DetectiveNovel' inherits from the class 'Book'. In the "DetectiveNovel" class, the 'getTitle' method is overridden, but not the toString method. class Book{ public String getTitle(){ return "book - "; } public String getPublisher(){ return "publisher"; } public String toString(){ return getTitle() + getPublisher(); } } class DetectiveNovel extends Book { @Override public String getTitle() { return "detective novel - "; } } When the "toString" method is called for a "DetectiveNovel"-type object, it always uses the "DetectiveNovel" class implementation for the "getTitle" method: public class DetectiveNovelTest { public static void main(String[] args) { Book book = new DetectiveNovel(); System.out.println(book.toString()); } } // 'Book' toString() // IMPLEMENTs getTitle() + getPublisher() // ie 'DetectiveNovel'/OVERRIDDEN getTitle() + 'Book'/ORIGINAL getPublisher() The program outputs: detective novel - publisher ========================================== Determining the Type of an Object As the TYPE of an OBJECT DETERMINES the outcome of a METHOD CALL in some situations, we need to be able to determine the type. This is done, for instance, with the operator 'instanceof'. The operator returns the value true, if the given object is of the given class type, i.e. A instanceof B obj1 instanceof Class1 variable1 instanceof Class1 returns the value true if A's type is B. An example clarifies the situation: public static void main(String[] args) { Person ollie = new Student("Oliver", "12345", 123); System.out.println(ollie instanceof Student); System.out.println(ollie instanceof Person); System.out.println(ollie instanceof Object); System.out.println(ollie instanceof Teacher); } The Program outputs: true true true false So, a "Student" type is both a "Student", "Person" and "Object". In fact, the statement A instanceof Object is true for all Java objects, as "Object" is the superclass of all classes. If there is a need TO DETERMINE the "ACTUAL" TYPE of an OBJECT and the type of the superclass is not sufficient, we can use the method "getClass": public static void main(String[] args) { Person oliver = new Student("Oliver", "12345", 123); System.out.println("Oliver is a Person: " + (oliver instanceof Person)); System.out.println("Oliveris a Person: " + (oliver.getClass() == Person.class)); System.out.println("Oliver is a Student: " + (oliver instanceof Student)); System.out.println("Oliver is a Student: " + (oliver.getClass() == Student.class)); } Ohjelma tulostaa: Oliver is a Person: true // 'Student' object type is a subclass of 'Person' Oliver is a Person: false // 'Student' object type was not initialised as a 'Person' class Oliver is a Student: true // 'Student' object type Oliver is a Student: true // 'Student' object type was initialised as a 'Student' class ==================== A typical example is the "equals" method, where it is determined whether an object is of the same type as the object being compared - see an example by generating the "equals" method in Eclipse! Let's consider an example of a method that receives a list of "Person"-class type objects and prints information about each object, whether it is a "Person", "Student" or "Teacher" object: // VARIABLE TYPE = 'Person' // OBJECT TYPE => depends on HOW OBJECTS WERE INITIALISED public static void whatType(ArrayList people) { for (Person person : people) { if (person.getClass() == Person.class) { System.out.println("This is a person!"); } else if (person.getClass() == Student.class) { System.out.println("This is a student!"); } else if (person.getClass() == Teacher.class) { System.out.println("This is a teacher!"); } } }