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 | |