File size: 6,074 Bytes
5ecac4f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
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<Person> 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!");
        }
    }
}