Polymorphism

in java

What is Polymorphism?

Poly means many.

Morphism means form.

In programing Polymorphism is the ability for objects of different classes related by inheritance to response differently to the same function call.

class Student {
    public String getClassName() {
        return "Student Class";
    }
}

class GraduateStudent extends Student {
    @Override public String getClassName() {
        return "GraduateStudent Class";
    }
}

class UndergraduateStudent extends Student {
    @Override public String getClassName() {
        return "UndergraduateStudent Class";
    }
}

public class TestPoly1 {
    public static void main(String[] args) {
	Student s = new UndergraduateStudent();
	System.out.println(s.getClassName());
    }
}

That’s polymorphism!!!

UnderGraduateStudent Class

Output:

public class TestNoPoly {
    public static void main(String[] args) {
        GraduateStudent gs = new GraduateStudent();
        displayGraduate(gs);
        displayUnder(new UndergraduateStudent());
    }
    static void displayGraduate(GraduateStudent gs) {
        System.out.println(gs.getClassName());
    }
    static void displayUnder(UndergraduateStudent us) {
        System.out.println(us.getClassName());
    }
}

Without Polymorphism

public class TestPoly2 {
    public static void main(String[] args){
 	display(new GraduateStudent());
 	display(new UndergraduateStudent());
    }

    //Polymorphic method
    public static void display(Student stu) {
        System.out.println(stu.getClassName());
    }
}

With Polymorphism

Why Polymorphism

  • To choose which method from which class is called at runtime dynamically. 
  • A variable of a parent type can refer to any child and grand child of the parent. 
  • If we don’t use polymorphism, you have to make a new method for every different parameter data type.
  • But with polymorphism, you make only one method for every parameter which has the same parent.

In Java, every element in an array has the same type, doesn’t it?

double[] scores = new double[10];
scores[0] = 1.4;
scores[1] = "Hi"; //Compile Error

Circle[] circles = new Circle[5];
circles[0] = new Circle(5);
circles[1] = new Circle(20);
circles[2] = new Student();//Compile Error

More Example —  Array

????

public class TestPoly3 {
    public static void main(String[] args) {
        Student[] s = new Student[3];
        s[0] = new Student();
        s[1] = new GraduateStudent();
        s[2] = new UndergraduateStudent();
        display(s); //Polymorphism
    }

    public static void display(Student[] s) {
        for(int i = 0; i<s.length; i++){
	    System.out.println(s[i].getClassName());
        }
    }
}

If I do want to store different types of objects in one array, how can I do it?

If the objects are inherited from the same parent, we can store them in the same array.

Polymorphism!!!

class GraduateStudent extends Student {
    public String getClassName() {
        return "GraduateStudent Class";
    }
    public String getDegree() {
        return "Msc in Computer Science";
    }
}

public class TestPoly4 {
    public static void main(String[] args){
	Student s = new GraduateStudent();
	System.out.println(s.getDegree());
    }
}

Now in a subclass GraduateStudent, we add a new method, say getDegree().

It will show a compile error because Student class doesn’t have getDegree().

What about if we want add a method, say getID() to the parent, Student.

class Student {
    public String getClassName() {
        return "Student Class";
    }
    public String getID() {
        return "NPIC001";
    }
}

public class TestPoly5 {
    public static void main(String[] args){
	Student s = new GraduateStudent();
	System.out.println(s.getID());
    }
}

No compile error. As long as getID() is in Student, we can call it without any question.

  • As long as subclasses inherit from the same parent, you can declare a variable as the parent type and then refer to any subclasses.
  • When you call a method which exists both in a parent and subclass, then it will dynamically refer to the method in the subclass.
  • You cannot call methods that are NOT existed in parent. If you try it, you will get compile error.
  • However, you can call every method in the superclass. 

Rules

  • Polymorphism is a mechanism to enable us to choose a method at runtime dynamically.
  • Polymorphism always needs Inheritance.
  • Polymorphism always needs Overridden Methods.
  • There is no keyword to indicate Polymorphism. 
  • You can recognise it whenever you see a class type that is different from its constructor.
  • You can also recognise it, when a class type is Object class.

Bullet Points

Casting

Grape g = new Grape();
g.getTaste();
g.getVitamin();
Fruit fruit = new Grape();
fruit.getTaste();
fruit.getVitamin();
Fruit fruit = new Grape();

Grape grape = (Grape) fruit;
grape.getTaste();
grape.getVitamin();

OK

Compile error

OK

public class TestNoCasting {
    public static void main(String[] args) {
        showTaste(new Grape());
    }
    public static void showTaste(Fruit fruit){
        JOptionPane.showMessageDialog(null, fruit.getTaste()); 
    }
}

Error because Fruit has not getTaste().

public class TestCastingObject {
    public static void main(String[] args) {
        showTaste(new Grape());
    }
    public static void showTaste(Fruit fruit) {
        Grape grape = (Grape) fruit;
        JOptionPane.showMessageDialog(null, grape.getTaste());
    }
}

OK.

public class TestCastingObject {
    public static void main(String[] args) {
        showTaste(new Apple());
    }
    public static void showTaste(Fruit fruit) {
        Grape grape = (Grape)fruit;
        JOptionPane.showMessageDialog(null, 
                grape.getTaste());
    }
}

Runtime Error: casting wrong class.

Instanceof

public static void main(String[] args) {
	showTaste(new Apple());
}
public static void showTaste(Fruit fruit) {
	if (fruit instanceof Grape) {
	    Grape grape = (Grape) fruit;	
            System.out.print(grape.getTaste());
	} else if(fruit instanceof Apple) {
	    Apple apple = (Apple) fruit;
            System.out.print(apple.getTaste());
        }
}

Use instanceof to test whether it is correct object or not before we cast it.

  • Casting is a technique to convert a variable from one class type to another one within an inheritance hierarchy. 
  • A keyword instanceof is an operation to check whether a class that we want to cast is in the correct hierarchy or not. 

Remember

Upcasting

Upcasting permits an object of a subclass type to be treated as an object of any superclass type.

Grape c = new Grape(); 
Fruit p = c; 
//Fruit p = new Grape();

When a variable of the base class (parent class) has a value of the derived class (child class), downcasting is possible.

Grape c = new Grape();
Fruit p = c;
Grape c2 = (Grape)p; 

Downcasting

Abstract Class

A class must be declared abstract when it has one or more abstract methods. A method is declared abstract when it has a method heading, but no body – which means that an abstract method has no implementation code inside curly braces like normal methods do.

public abstract class Figure {
    /* because this is an abstract method 
    the body will be blank  */
    public abstract float getArea();	
}

public class Circle extends Figure {
    private float radius;

    public float getArea() {
	return (3.14 * (radius * 2)); 	
    }
}

public class Rectangle extends Figure {
    private float length, width;

    public float getArea(Figure other) {
	return length * width;
    }
}

Abstract classes are meant to be inherited from, and when one class inherits from another it means that there is a strong relationship between the 2 classes.

So, we can summarize this first point by saying that an abstract class would be more appropriate when there is a strong relationship between the abstract class and the classes that will derive from it. Again, this is because an abstract class is very closely linked to inheritance, which implies a strong relationship.

Interface

An interface differs from an abstract class because an interface is not a class. An interface is essentially a type that can be satisfied by any class that implements the interface. And it's not necessarily a strong relation.

public interface Refrigerator {
    public void Freeze();
}

public class MyHome implements Refrigerator {
    public void Freeze() {
        //method definition here
    }
}

Any class that implements an interface must satisfy 2 conditions:

Interfaces are a good substitute for multiple inheritance

Java does not allow multiple inheritance. In Java, a class can only derive from one class, whether it’s abstract or not. However, a class can implement multiple interfaces – which could be considered as an alternative to for multiple inheritance. So, one major difference is that a Java class can inherit from only one abstract class, but can implement multiple interfaces.

  • It must have the phrase "implements Interface_Name" at the beginning of the class definition.
  • It must implement all of the method headings listed in the interface definition.

Abstract Class v.s. Interface

When to use abstract class and interface in Java

  • An abstract class is good if you think you will plan on using inheritance since it provides a common base class implementation to derived classes.
  • An abstract class is also good if you want to be able to declare non-public members. In an interface, all methods must be public.
  • If you think you will need to add methods in the future, then an abstract class is a better choice. Because if you add new method headings to an interface, then all of the classes that already implement that interface will have to be changed to implement the new methods. That can be quite a hassle.
  • Interfaces are a good choice when you think that the API will not change for a while.
  • Interfaces are also good when you want to have something similar to multiple inheritance, since you can implement multiple interfaces.

Inner Class

  • Static Nested Classes
  • Member Inner Class
  • Method-Local Inner Classes
  • Anonymous Inner Classes

Static Nested Classes

class Outer {
    static class Inner {
        void go() {
	    System.out.println("Inner class reference is: " + this);
	}
    }
}
 
public class Test {
    public static void main(String[] args) {
	Outer.Inner n = new Outer.Inner();
	n.go();
    }
}

Inner class reference is: Outer$Inner@19e7ce87

Member Inner Class

public class Outer {
    private int x = 100;
 
    public void makeInner() {
        Inner in = new Inner();
        in.seeOuter();
    }
 
    class Inner {
        public void seeOuter(){
            System.out.println("Outer x is " + x);
            System.out.println("Inner class reference is " + this);
            System.out.println("Outer class reference is " + Outer.this);
        }
    }
 
    public static void main(String [] args) {
    	Outer o = new Outer();
        Inner i = o.new Inner();
        i.seeOuter();
    }
}

Outer x is 100
Inner class reference is Outer$Inner@4dfd9726
Outer class reference is Outer@43ce67ca​

Method-Local Inner Classes

public class Outer {
    private String x = "outer";
 
    public void doStuff() {
        class MyInner {
            public void seeOuter() {
                System.out.println("x is " + x);
	    }
	}
 
	MyInner i = new MyInner();
	i.seeOuter();
    }
 
    public static void main(String[] args) {
	Outer o = new Outer();
	o.doStuff();
    }
}

x is outer

public class Outer {
    private static String x = "static outer";
 
    public static void doStuff() {
	class MyInner {
	    public void seeOuter() {
		System.out.println("x is " + x);
	    }
	}
 
	MyInner i = new MyInner();
	i.seeOuter();
    }
 
    public static void main(String[] args) {
	Outer.doStuff();
    }
}

x is static outer.

Anonymous Inner Classes

button.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e){
         comp.setText("Button has been clicked");
     }
});

This is frequently used when you add an action listener to a widget in a GUI application.

Polymorphism

By TingSheng Lee

Polymorphism

  • 405