Spaces:
Running
Running
Polymorphism may sound like a complex term, but in practice, it is a rather simple concept of object-oriented programming. | |
It means that the SAME OBJECT can be REFERENCED BY using DIFFERENT TYPE VARIABLES. | |
Since ALL CLASSES in Java INHERIT (directly or indirectly) class 'Object', an 'Object' type variable CAN REFERENCE ANY OBJECT in Java: | |
public static void main(String[] args) { | |
Object string = "Hi all!"; | |
Object student = new Student("1234", "Sarah Student", 4); | |
Object list = new ArrayList<Integer>(); | |
} | |
================================================================ | |
Then why wont we use 'Object' type variables only? | |
The 'TYPE' of the VARIABLE DEFINES WHAT MEMBERS CAN BE REFERENCED using that variable. | |
Since there is only a handful of methods defined in the class 'Object' (for example 'toString' and 'equals'), | |
we can only REFERENCE THOSE METHODS when the type of the variable is Object. | |
public static void main(String[] args) { | |
Object string = "Hi all!"; | |
Object student = new Student("1234", "Sarah Student", 4); | |
Object list = new ArrayList<Integer>(); | |
System.out.println(string.toString()); | |
System.out.println(student.toString()); | |
System.out.println(list.toString()); | |
} | |
Program output | |
Hi all! | |
Sarah Student (1234), 4 op. | |
[] | |
Even if your Student/Opiskelija class/object has other methods, | |
trying to reference ur other/custom methods provides an error message, | |
if the members are not declared in the 'variable type class': | |
****************************************** | |
=> WHAT DOES THIS MEAN??????? | |
*******TO CLARIFY************** | |
=> members (of a class) = 1. variables/fields, 2. methods/fns | |
=> members not declared => variable, method not declared in the 'reference type' class (i.e. the 'Object' class over here) | |
=> 'variable type class' = class/type for a variable | |
eg | |
Animal animal = new Dog(); | |
Animal = class/type = 'variable type class' | |
animal = variable | |
compiler knows 'animal' is an Animal object | |
compiler does not know 'animal' is a Dog object | |
****************************************** | |
Object student = new Opiskelija("1234", "Sarah Student", 4); | |
// getName method is not defined in 'Object' class | |
// even if actual object of 'Student/Opiskelija' class contains the 'getName' method => we still get compiler error | |
String name = student.getName(); | |
Program produces a compiler error | |
The method getName() is 'undefined' for the type Object | |
================================================================ | |
Why Polymorphism? | |
Since an object can be REFERENCED BY its OWN TYPE variables and by ALL SUPERTYPE VARIABLES, | |
we can for example write a method that works on several different 'type objects'. | |
****************************************** | |
=> WHAT DOES THIS MEAN??????? | |
reference type vs actual object type | |
eg | |
class Animal { } | |
class Dog extends Animal { } | |
// => dog is of type Dog, so you can call all methods defined in Dog. | |
Dog dog = new Dog(); // Own type | |
// => animal is of type Animal, but holds a Dog. You can only access methods declared in Animal. | |
Animal animal = new Dog(); // Supertype reference | |
// => obj is of type Object, but holds a Dog. Again, limited to Object methods unless cast. | |
Object obj = new Dog(); // Also a supertype (since Object is the root) | |
So the same 'Dog' object can be referenced by : | |
Its OWN TYPE : Dog | |
ANY SUPERTYPE : Animal, Object | |
=> also what does type object/s mean? | |
=>'type object' = 1 method work on objects of different classes, as long as related thru inheritance | |
=> so method allows supertype OR subtypes | |
eg | |
class Animal { | |
void makeSound() { | |
System.out.println("Some sound"); | |
} | |
} | |
class Dog extends Animal { | |
void makeSound() { | |
System.out.println("Bark!"); | |
} | |
} | |
class Cat extends Animal { | |
void makeSound() { | |
System.out.println("Meow!"); | |
} | |
} | |
public class Test { | |
public static void main(String[] args) { | |
// 1 ...OTHER CODE... | |
// 3 calling the method | |
//Even though the method expects an Animal, it can accept any subclass (Dog, Cat) because they are subtypes of Animal. | |
//polymorphism = one interface, many forms | |
letAnimalMakeSound(new Dog()); // Bark! | |
letAnimalMakeSound(new Cat()); // Meow! | |
letAnimalMakeSound(new Animal()); // Some sound | |
} | |
// 2 THEN DEFINE REUSABLE METHOD ACROSS CLASSES | |
void letAnimalMakeSound(Animal animal) { | |
animal.makeSound(); | |
} | |
} | |
****************************************** | |
Let's consider a class hierarchy presented last week. | |
We have a class 'Person' which is inherited by classes 'Student' and 'Teacher'. Here are summaries of the classes: | |
class Person { | |
private String name; | |
private String email; | |
public Person(String name, String email) { | |
this.name = name; | |
this.email = email; | |
} | |
public String getName() { | |
return name; | |
} | |
public String getEmail() { | |
return email; | |
} | |
} | |
class Teacher extends Person { | |
private int hours; | |
public Teacher(String name, String email, int hours) { | |
super(name, email); | |
this.hours = hours; | |
} | |
public int getHours() { | |
return hours; | |
} | |
} | |
class Student extends Person { | |
private int credits; | |
public Student(String name, String email, int credits) { | |
super(name, email); | |
this.credits = credits; | |
} | |
public int getCredits() { | |
return credits; | |
} | |
} | |
THEN | |
Now we can write a method that receives a 'Person' type object as an ARGUMENT. | |
The method outputs the email address of the person. | |
=> 'outputEmail method' below | |
In addition to 'Person' type objects, the method can receive 'Student' and 'Teacher' type objects as ARGUMENTS. | |
This is due to the fact that students and teachers are also persons in our class hierarchy. | |
public class Test { | |
public static void main(String[] args) { | |
Person person = new Person("Eric Example", "eric@example.com"); | |
outputEmail(person); | |
Teacher teacher = new Teacher("Tim Teacher", "tim@example.com", 25); | |
outputEmail(teacher); | |
Student student = new Student("Sarah Student", "sarah@example.com", 241); | |
outputEmail(student); | |
} | |
// DEFINE REUSABLE METHOD ACROSS CLASSES HERE | |
public static void outputEmail(Person person) { | |
System.out.println(person.getEmail()); | |
} | |
} | |
Program output | |
eric@example.com | |
tim@example.com | |
sarah@example.com | |
VS | |
Similarly, we could create a 'Person' type list and save 'Person, Teacher and Student' objects into it: | |
Person person = new Person("Eric Example", "eric@example.com"); | |
Teacher teacher = new Teacher("Tim Teacher", "tim@example.com", 25); | |
Student student = new Student("Sarah Student", "sarah@example.com", 241); | |
ArrayList<Person> persons = new ArrayList<>(); | |
persons.add(person)); | |
persons.add(teacher); | |
persons.add(student); | |
VS | |
Now we can iterate the list and call the method 'getName' for all objects. | |
Again, it does not matter if the type of the object is Person, Teacher, or Student; | |
they all have the getName method. | |
=> BECAUSE 'getName' WAS DEFINED IN THE PARENT CLASS 'Person' | |
for (Person p : persons) { | |
System.out.println(p.getName()); | |
} | |
Program output | |
Eric Example | |
Tim Teacher | |
Sarah Student | |