하위 클래스를 상위 클래스의 데이터 타입으로 인스턴스화 했을 때 어떤 일이 일어나는지에 대해서는 어느정도 이해했을꺼라고 생각한다. 하지만 가장 큰 틀의 질문은 이걸 어디에 사용하는가?일것이다. 정당한 질문이다. abstract 수업의 예제 코드를 조금 변경해보자.
package org.opentutorials.javatutorials.polymorphism; abstract class Calculator{ int left, right; public void setOprands(int left, int right){ this.left = left; this.right = right; } int _sum() { return this.left + this.right; } public abstract void sum(); public abstract void avg(); public void run(){ sum(); avg(); } } class CalculatorDecoPlus extends Calculator { public void sum(){ System.out.println("+ sum :"+_sum()); } public void avg(){ System.out.println("+ avg :"+(this.left+this.right)/2); } } class CalculatorDecoMinus extends Calculator { public void sum(){ System.out.println("- sum :"+_sum()); } public void avg(){ System.out.println("- avg :"+(this.left+this.right)/2); } } public class CalculatorDemo { public static void main(String[] args) { Calculator c1 = new CalculatorDecoPlus(); c1.setOprands(10, 20); c1.run(); Calculator c2 = new CalculatorDecoMinus(); c2.setOprands(10, 20); c2.run(); } }
차이점은 아래와 같다. 아래는 예전 코드다.

아래는 변경된 코드의 내용이다.

차이점은 Calculator를 상속 받은 클래스들을 인스턴스화 할 때 Calculator를 데이터 타입으로 하고 있다. 이렇게 되면 인스턴스 c1과 c2를 사용하는 입장에서 두개의 클래스 모두 Calculator인 것처럼 사용할 수 있다. 예제를 조금 수정해보자.
package org.opentutorials.javatutorials.polymorphism; abstract class Calculator{ int left, right; public void setOprands(int left, int right){ this.left = left; this.right = right; } int _sum() { return this.left + this.right; } public abstract void sum(); public abstract void avg(); public void run(){ sum(); avg(); } } class CalculatorDecoPlus extends Calculator { public void sum(){ System.out.println("+ sum :"+_sum()); } public void avg(){ System.out.println("+ avg :"+(this.left+this.right)/2); } } class CalculatorDecoMinus extends Calculator { public void sum(){ System.out.println("- sum :"+_sum()); } public void avg(){ System.out.println("- avg :"+(this.left+this.right)/2); } } public class CalculatorDemo { public static void execute(Calculator cal){ System.out.println("실행결과"); cal.run(); } public static void main(String[] args) { Calculator c1 = new CalculatorDecoPlus(); c1.setOprands(10, 20); Calculator c2 = new CalculatorDecoMinus(); c2.setOprands(10, 20); execute(c1); execute(c2); } }
차이점은 아래와 같다.

클래스 CalculatorDemo의 execute 메소드는 CalculatorDecoPlus와 CalculatorDecoMinus 클래스의 메소드 run을 호출하면서 그것이 '실행결과'라는 사실을 화면에 표시하는 기능을 가지고 있다. 이 때 메소드 execute 내부에서는 매개변수로 전달된 객체의 메소드 run을 호출하고 있다.
만약 메소드 execute의 매개변수 데이터 타입이 Calculator가 아니라면 어떻게 해야할까? 위와 같은 로직을 처리 할 수 없을 것이다. 메소드 execute 입장에서는 매개변수로 전달된 값이 Calculator이거나 그 자식이라면 메소드 run을 가지고 있다는 것을 보장 받을 수 있게 되는 것이다.
이 맥락에서의 다형성이란 하나의 클래스(Calculator)가 다양한 동작 방법(ClaculatorDecoPlus, ClaculatorDecoMinus)을 가지고 있는데 이것을 다형성이라고 할 수 있겠다.