다형성 활용

한라봉봉·2025년 7월 29일

JAVA

목록 보기
14/16

1. 먼저 다형성을 사용하지 않고 예제코드를 작성

단순히 개, 고양이, 소 동물들의 울음 소리를 출력하는 프로그램이다.

public class Dog {
    public void sound() {
     System.out.println("멍멍");
    }
 }
public class Cat {
	public void sound() {
		System.out.println("냐옹");
   }
}
public class Cow {
	public void sound() {
		System.out.println("음매");
   }
}

아래 코드에서 중복을 제거할 수 있을까?

 public class AnimalSoundMain {
   public static void main(String[] args) {
     Dog dog = new Dog();
     Cat cat = new Cat();
     Cow cow = new Cow();
     System.out.println("동물 소리 테스트 시작");
     dog.sound();
     System.out.println("동물 소리 테스트 종료");
     System.out.println("동물 소리 테스트 시작");
     cat.sound();
     System.out.println("동물 소리 테스트 종료");
     System.out.println("동물 소리 테스트 시작");
     cow.sound();
     System.out.println("동물 소리 테스트 종료");
   }
 }

메서드로 중복 제거 시도: 매개변수가 Cow로 고정돼서 Dog, Cat을 인수로 받아 재사용할수 없다.

private static void soundCow(Cow cow) {
 System.out.println("동물 소리 테스트 시작");
 cow.sound();
 System.out.println("동물 소리 테스트 종료");
}

열과 for문을 통한 중복 제거 시도: 배열의 타입을 Dog, Cat, Cow중 하나로 지정해야해서 불가

 Caw[] cowArr = {cat, dog, caw}; //컴파일 오류 발생!
 System.out.println("동물 소리 테스트 시작");
 for (Caw caw : cowArr) {
        cowArr.sound();
 }
 System.out.println("동물 소리 테스트 종료");

문제의 핵심은 바로 타입이 다르다는 점. 다형적 참조와 메서드 오버라이딩을 활용하면 Dog, Cat, Cow가 모두 같은 타입을 사용하고, 각자 자신의 메서드도 호출할 수 있다.

2. 다형성을 사용하도록 예제코드를 변경(메서드 재사용)

Dog, Cat, Cow 클래스를 모두 담을수 있는 부모 Animal을 만듦.

Dog, Cat, Cow 클래스는 public class Cat extends Animal 과 같이 Animal을 상속.

 public class Animal {
   public void sound() {
   	System.out.println("동물 울음 소리");
      }
 }

새로운 클래스를 정의하더라도 재사용할 수 있는 코드 작성

public class AnimalPolyMain1 {
 public static void main(String[] args) {
 Dog dog = new Dog();
 Cat cat = new Cat();
 Cow cow = new Cow();
 soundAnimal(dog);
 soundAnimal(cat);
 soundAnimal(cow);
    }
 //동물이 추가 되어도 변하지 않는 코드
private static void soundAnimal(Animal animal) {
 System.out.println("동물 소리 테스트 시작");
        animal.sound();
 System.out.println("동물 소리 테스트 종료");
    }
 }

코드분석

  • soundAnimal(dog)을 호출하면 soundAnimal(Animal animal)에 Dog 인스턴스가 전달된다.
    - Animal animal = dog로 이해하면 된다.
  • soundAnimal() 메서드 안에서 animal.sound() 메서드를 호출한다.
  • animal 변수의 타입은 Animal이므로 Dog 인스턴스에 있는 Animal 클래스 부분에서 sound() 메서드 호출을 시도한다.
  • 하위 클래스인 Dog에서 sound() 메서드를 오버라이딩 했으므로 우선권을 갖고, 호출된다.
  • Animal의 sound()는 실행되지 않는다.

이 코드의 핵심은 Animal animal 부분이다.

  • 다형적 참조 덕분에 animal 매개변수는 자식인 Dog, Cat, Cow의 인스턴스를 참조할 수 있다.
  • 메서드 오버라이딩 덕분에 animal.sound()를 호출해도 Dog.sound(), Cat.sound(), Cow.sound() 와 같이 각 인스턴스의 메서드를 호출할 수 있다. 자바에 메서드 오버라이딩이 없었다면 모두 Animal의 sound()가 호출되었을 것이다.

3. 다형성을 사용하도록 예제코드를 변경(배열과 for문 추가 사용)

Animal[] animalArr 를 통해서 배열을 사용한다.
soundAnimal(Animal animal) 하나의 동물을 받아서 로직을 처리한다.

public class AnimalPolyMain3 {
	public static void main(String[] args) {
       Animal[] animalArr = {new Dog(), new Cat(), new Cow()};
       for (Animal animal : animalArr) {
          soundAnimal(animal);
       }
    }
   //동물이 추가 되어도 변하지 않는 코드
   private static void soundAnimal(Animal animal) {
     System.out.println("동물 소리 테스트 시작");
     animal.sound();
     System.out.println("동물 소리 테스트 종료");
   }
}

4. 남은 문제

  • Animal 클래스 인스턴스를 생성할 수 있는 문제
    Cat, Dog와 달리 Animal은 추상적인 개념이지 실제로 존재하지 않는다. 그러나 제약없는 클래스이기 때문에 누군가 실수로 new Animal() 을 사용하여 인스턴스를 생성할 수 있다.

  • 클래스를 상속 받는 곳에서 sound() 메서드 오버라이딩을 하지 않을 가능성
    이렇게 되면 부모의 기능을 상속 받는다. 부모 클래스에 있는 sound()가 출력되어 버린다.

-> 좋은 프로그램은 제약이 있는 프로그램이다. 추상클래스와 추상메서드를 사용하여 이런 문제를 해결할 수 있다.

profile
백엔드 개발공부 로그를 기록합니다

0개의 댓글