logo

Java의 다형성

Java의 다형성 수행할 수 있는 개념입니다. 다양한 방식으로 단일 작업 . 다형성은 2개의 그리스어 단어인 폴리(poly)와 모르프(morphs)에서 파생됩니다. 폴리(poly)는 많다는 뜻이고 모프(morphs)는 형태라는 뜻이다. 따라서 다형성은 다양한 형태를 의미합니다.

Java에는 컴파일 타임 다형성과 런타임 다형성이라는 두 가지 유형의 다형성이 있습니다. 자바에서는 메소드 오버로딩과 메소드 오버라이딩을 통해 다형성을 수행할 수 있습니다.

Java에서 정적 메소드를 오버로드하는 경우 이는 컴파일 시간 다형성의 예입니다. 여기서는 Java의 런타임 다형성에 중점을 둘 것입니다.


Java의 런타임 다형성

런타임 다형성 또는 동적 메소드 디스패치 재정의된 메서드에 대한 호출이 컴파일 타임이 아닌 런타임에 해결되는 프로세스입니다.

이 과정에서 슈퍼클래스의 참조변수를 통해 오버라이드된 메소드가 호출된다. 호출할 메서드는 참조 변수가 참조하는 개체에 따라 결정됩니다.

먼저 런타임 다형성 이전의 업캐스팅을 이해해 보겠습니다.

업캐스팅

Parent 클래스의 참조 변수가 Child 클래스의 객체를 참조하는 것을 업캐스팅이라고 합니다. 예를 들어:

Java에서 업캐스팅
 class A{} class B extends A{} 
 A a=new B();//upcasting 

업캐스팅을 위해서는 클래스 유형이나 인터페이스 유형의 참조 변수를 사용할 수 있습니다. 예를 들어:

 interface I{} class A{} class B extends A implements I{} 

여기서 B 클래스의 관계는 다음과 같습니다.

 B IS-A A B IS-A I B IS-A Object 

Object는 Java의 모든 클래스의 루트 클래스이므로 B IS-A Object를 작성할 수 있습니다.


Java 런타임 다형성의 예

이 예에서는 Bike와 Splendor라는 두 가지 클래스를 생성합니다. Splendor 클래스는 Bike 클래스를 확장하고 해당 run() 메서드를 재정의합니다. Parent 클래스의 참조 변수로 run 메소드를 호출합니다. 하위 클래스 객체를 참조하고 하위 클래스 메서드가 상위 클래스 메서드를 재정의하므로 하위 클래스 메서드는 런타임에 호출됩니다.

메소드 호출은 컴파일러가 아닌 JVM에 의해 결정되므로 이를 런타임 다형성이라고 합니다.

행운을 빌어요
 class Bike{ void run(){System.out.println('running');} } class Splendor extends Bike{ void run(){System.out.println('running safely with 60km');} public static void main(String args[]){ Bike b = new Splendor();//upcasting b.run(); } } 
지금 테스트해보세요

산출:

 running safely with 60km. 

Java 런타임 다형성 예: 은행

Bank가 이자율을 구하는 메서드를 제공하는 클래스인 시나리오를 생각해 보세요. 다만, 은행별로 이자율이 다를 수 있습니다. 예를 들어 SBI, ICICI, AXIS 은행은 8.4%, 7.3%, 9.7%의 이자율을 제공하고 있습니다.

은행의 Java 런타임 다형성 예

참고: 이 예는 메서드 재정의에도 제공되지만 업캐스팅은 없습니다.

 class Bank{ float getRateOfInterest(){return 0;} } class SBI extends Bank{ float getRateOfInterest(){return 8.4f;} } class ICICI extends Bank{ float getRateOfInterest(){return 7.3f;} } class AXIS extends Bank{ float getRateOfInterest(){return 9.7f;} } class TestPolymorphism{ public static void main(String args[]){ Bank b; b=new SBI(); System.out.println('SBI Rate of Interest: '+b.getRateOfInterest()); b=new ICICI(); System.out.println('ICICI Rate of Interest: '+b.getRateOfInterest()); b=new AXIS(); System.out.println('AXIS Rate of Interest: '+b.getRateOfInterest()); } } 
지금 테스트해보세요

산출:

 SBI Rate of Interest: 8.4 ICICI Rate of Interest: 7.3 AXIS Rate of Interest: 9.7 

Java 런타임 다형성 예: 모양

 class Shape{ void draw(){System.out.println('drawing...');} } class Rectangle extends Shape{ void draw(){System.out.println('drawing rectangle...');} } class Circle extends Shape{ void draw(){System.out.println('drawing circle...');} } class Triangle extends Shape{ void draw(){System.out.println('drawing triangle...');} } class TestPolymorphism2{ public static void main(String args[]){ Shape s; s=new Rectangle(); s.draw(); s=new Circle(); s.draw(); s=new Triangle(); s.draw(); } } 
지금 테스트해보세요

산출:

 drawing rectangle... drawing circle... drawing triangle... 

Java 런타임 다형성 예: 동물

 class Animal{ void eat(){System.out.println('eating...');} } class Dog extends Animal{ void eat(){System.out.println('eating bread...');} } class Cat extends Animal{ void eat(){System.out.println('eating rat...');} } class Lion extends Animal{ void eat(){System.out.println('eating meat...');} } class TestPolymorphism3{ public static void main(String[] args){ Animal a; a=new Dog(); a.eat(); a=new Cat(); a.eat(); a=new Lion(); a.eat(); }} 
지금 테스트해보세요

산출:

 eating bread... eating rat... eating meat... 

데이터 멤버를 사용한 Java 런타임 다형성

데이터 멤버가 아닌 메서드가 재정의되므로 데이터 멤버로는 런타임 다형성을 달성할 수 없습니다.

아래 예제에서 두 클래스 모두 데이터 멤버 속도 제한이 있습니다. 하위 클래스 객체를 참조하는 Parent 클래스의 참조 변수를 통해 데이터 멤버에 액세스하고 있습니다. 재정의되지 않은 데이터 멤버에 액세스하고 있으므로 항상 Parent 클래스의 데이터 멤버에 액세스합니다.

규칙: 런타임 다형성은 데이터 멤버로 달성할 수 없습니다.

 class Bike{ int speedlimit=90; } class Honda3 extends Bike{ int speedlimit=150; public static void main(String args[]){ Bike obj=new Honda3(); System.out.println(obj.speedlimit);//90 } } 
지금 테스트해보세요

산출:

 90 

다중 레벨 상속을 통한 Java 런타임 다형성

다단계 상속을 사용하는 런타임 다형성의 간단한 예를 살펴보겠습니다.

 class Animal{ void eat(){System.out.println('eating');} } class Dog extends Animal{ void eat(){System.out.println('eating fruits');} } class BabyDog extends Dog{ void eat(){System.out.println('drinking milk');} public static void main(String args[]){ Animal a1,a2,a3; a1=new Animal(); a2=new Dog(); a3=new BabyDog(); a1.eat(); a2.eat(); a3.eat(); } } 
지금 테스트해보세요

산출:

 eating eating fruits drinking Milk 

출력을 시도

 class Animal{ void eat(){System.out.println('animal is eating...');} } class Dog extends Animal{ void eat(){System.out.println('dog is eating...');} } class BabyDog1 extends Dog{ public static void main(String args[]){ Animal a=new BabyDog1(); a.eat(); }} 
지금 테스트해보세요

산출:

 Dog is eating 

BabyDog은 eat() 메서드를 재정의하지 않으므로 Dog 클래스의 eat() 메서드가 호출됩니다.