Java/Java 활용 개념 정리

[Java] 객체 지향의 꽃! 다형성(message polymorphism)

Dev dreamer 2023. 2. 4. 11:46

 

🔍일상생활에서 보는 다형성!

여러가지의 브랜드의 다양한 TV(자식클래스)가 있다고 해보자. 그 tv(부모클래스)를 작동하는 리모콘이 있다.

 

리모콘을 통해 TV의 볼륨(메소드)을 조절하려고 한다.

 

 

                           ↗                         ➡          삼성 TV     ➡          🆗⬆⬆⬆⬆⬆⬆⬆

리 모 콘 ➡ 볼륨 up    ➡      Dev TV     ➡       1️⃣2️⃣3️⃣

                           ↘                       ➡          엘지 TV      ➡          ⏫⏫⏫

 

 

같은 볼륨up 이라는 메서드를 구현했어도 각 TV마다 화면에 각자마다의 표현(재정의)으로 보일것이다.

 

 

 

💡 1. 다형성 (Message polymrphism)


상속 관계에 있는 클래스에서 상위클래스가 동일한 메세지로 하위클래스들을

서로 다르게 동작시키는 객체지향의 원리(개념)

 

 

 

 

 

이러한 상속 관계에서 message polymorphism(다형성)의 경우를 살펴보자.

 

상위 클래스인 Animal이 하위클래스인 Dog와 Cat에게 동일하게 먹어라(eat)라고 메세지를 보냈을 때

하위 클래스인 Dog와 Cat의 eat()메서드가 서로 다르게 동작되는 객체지향 원리

 

Animal(부모클래스) 왈 먹어라 eat() !

     ⬇

Dog.class Cat.class의 eat()!

하위 클래스의 동작 방식을 알 수 없어도 상위클래스를 통해 하위클래스를 구동 시킬 수 있다.

 

 

 

 

Animal 형을가진 변수 r 에 new Dog 생성자로 같은 메모리에서 Anmal과 Dog가 함께 객체가 생성되면

r의 변수로는 사실상 Animal 객체에만 접근이 가능 하지만 Animal 객체에 eat()을 실행하게 되면

실행 되는 시점에서

 

어? 같이 생성된내 자식 객체에 같은 메서드가 있나 볼까? 있으면 자식이 오버라이딩 한 메서드가 실행됨.

 

 

 

 

Animal(부모클래스)에 night() 메서드가 없다고 해도 downcasting 을 통해 해당 메서드를 사용할 수 있다.

 

 

 

 

 

 

 

 

💡 2. 다형성 이론의 전제조건


➡  다형성이 적용되기 위해서는 상속관계가 되어야 한다. 

 

➡  객체생성을 upcasting으로 할 것(상위클래스가 하위클래스에게 메세지를 보내야 하므로)

      (upcasting이 되면 downcasting을 할 수 있다.) ➡ A a = new B();

 

  다형성이 보장되기 위해서는 하위클래스가 반드시 재정의(override)해야 한다.

 

➡ 동적 바인딩을 통해 실현된다.

(동적 바인딩: 실행시점에서 사용될 메서드가 결정되는 바인딩, 프로그램 속도를 떨어뜨리는 원인이 된다.)

 

 

💡 3. 다형성 활용 방법


🔍 3.1 다형성 인수(데이터 이동)

Dog 객체와 Cat이라는 객체가 있다.

그리고 display 라는 객체가 있다. 이 display에 Dog타입의 d 와 Cat 타입의 c 를 display로 보내고 싶다.

 

이 경우에

public static void display(Dog r){ r.eat() }

public static void display(Cat r){ r.eat() }

 

이런식으로 display 객체를 여러개 만들어야 할 것이다.

 

하지만 부모클래스인 Animal을 이용하면 하나만 만들어도 Dog와 Cat이 이용이 가능하다.

 

 

 

🔍 원리 이해하기!

Dog d=new Dog 이다.

Cat c=new Cat 이다.

 

이때 d 와 c 를 매개변수로 넣어서 dispaly로 전달하는데

Animal r = d = new Dog

Animal r = c= new Cat

 

이라고 생각해보면

 

Animal r = new Dog (upcasting)

Animal r = new Cat (upcasting) 이 되는 것이다.

 

이러한 자식 클래스들이 한번에 접근 가능한 것을 다형성 인수 라고 한다.

 

 

 

 

Animal의 r 이라는 변수로 매개변수를 설정해 뒀어도

상속관계인 Cat의 c 와 Dog의 d 로 (override)한 메소드로 접근 및 실행이 가능하다.

 

여기서!!

 

 

 

 

Cat 타입으로 downcasting 해서 night 로 접근하려면 문제가 생긴다.

Dog 타입의 경우 night 라는 메소드가 아예 없기 때문에 오류가 발생한것이다.( 에러전의 개처럼먹다 까지만 출력됐다.)

 

따라서 하나의 조건이 필요하다.

 

 

 

instance of  : 연산자는 객체가 어떤 클래스인지, 어떤 클래스를 상속받았는지 확인하는데 사용하는 연산자입니다.

r instanceof Cat => r이 Cat의 클래스이면 true를 반환하고 아니면 false를 반환한다.

 

이를 통해 Dog 클래스가 들어온 시점에서는 if 문을 그냥 지나칠 것이고

Cat의 경우 r.eat이 동작후에 if문에서 true가 나와 ((Cat)r.night();를 실행시킬 것이다.

 

 

 

여기서 헷갈리지 말아야 할점!

upcasting 같은걸 진행 안했는데도 downcasting 이 되네요 ?

 

부모 클래스 형태로 upcastiong 을 통해서 메서드에 접근이 가능하다는거지

꼭 반드시 upcasting을 통해서 부모클래스에 접근할 필요는 없다.

 

자식 클래스에서 바로 생성한 객체는 생성자 메서드의 super을 통해서 부모클래스를 함께 생성해

부모객체에도 모두 접근이 가능하기 때문에 재정의한 eat()를 출력할 수 있는 것이다.

 

 

 

🔍 3.2 다형성 배열(서로 다른 객체를 담을 수 있다.)

 

 

 

보통 upcasting 을 이용한다고 하면

 

Animal r = new Dog();r.eat();r=new Cat();r.eat();

 

이렇게 Dog를 한번 upcasting 해주고 또 그 r에 Cat을 upcasting 해줘서 출력했다.이 경우 다시 Dog 의 eat() 메서드를 출력하고자 할때 또 다시 출력해야 하는 것이다.

 

이러한 부분의 불편함을 극복할 수 있는 방법이 있다.

 

Animal[] r = new Animal[2];r[0]=new Cat();r[1]=new Dog(); 

 

이렇게 배열로 선언한 Animal 클래스의 객체 변수에 각 Cat 과 Dog 를 upcasting 해서 담고필요할 때 해당 값을 이용해주면 되는것이다.

r[0].eat(); // 고양이처럼 먹는다

r[1].eat(); // 개처럼 먹는다.

 

이러한 경우가 많은 경우 배열의 특성을 이용해서 반복문을 이용할 수 있게 된다.

 

 

 

 

 

배열에 저장한 upcasting 한 Dog 와 Cat들을

for 문을 통해 출력이 가능해 진다.

 

 

 

 

마찬가지로 Cat에만 있는 night 라는 메서드를 downcasting 을 통해 접근하고자 할때

instanceof의 if 문을 사용해서 Cat일때만 downcasting을 해주면 된다.

 

or 배열의 경우 ((Cat)ani[1]).night(); 를 통해서  (Cat 이 몇번째 배열에 있는지 파악후) 직접입력도 가능하다.

 

 

 

부모 클래스로 접근 할 때 부모의 배열에 담아서 전송도 가능하다.

 

 

 

 

Java 관련 개념을 알려주실 때 그 내부 동작이 어떻게 구현되는지 자세하게 알수 있는 강의입니다.

출처: Java TPC (생각하고, 표현하고, 코딩하고) 대시보드 - 인프런 | 강의 (inflearn.com)