Java 다형성 - extends 상속 문제점

이전에 Animal 클래스를 사용하여 다형성의 장점에 대해서 설명하였다. 그렇지만 남아있는 문제가 있는데, extends 상속을 사용하면서 Animal 추상화된 개념을 생성할 가능성이 있다는 것이다.


상속 문제는?

첫째, Animal 클래스를 생성할 가능성
둘째, Animal 클래스 상속받은 곳에서 sound() 오버라이딩 누락할 가능성

코드를 살펴보자

Snake.java

package sound;

public class Snake extends Animal {
    // sound 오버라이딩 메소드를 생성을 실수로 누락함
}

AnimalMain.java

package sound;

public class AnimalMain {
    public static void main(String[] args) {

        Animal[] animals = { new Dog(), new Cat(), new Bird(), new Cow(), new Snake() };
        for (Animal animal : animals) {
            soundAnimal(animal);
        }
    }

    private static void soundAnimal(Animal animal) {
        System.out.println("동물 울음 소리: " + animal.sound());
    }
}

실행결과

동물 울음 소리: 멍멍
동물 울음 소리: 냐옹
동물 울음 소리: 짹짹
동물 울음 소리: 음머에~
동물 울음 소리: 동물 소리

Snake.sound() 호출하여 동물 울음 소리를 표현하였더니 "동물 소리" 문자열로 받게 되었다. 기능을 수행 못한 케이스.

별 다른 문제가 없어 보이지만, 코드는 협업하여 작성하는 것이다. 심지어 테스트에도 정상적인 문자열을 받았네~ 판단하여 통과한 경우 이러한 문제는 나중에 발견된다. 시간이 지나서 발견한 경우 수정하는 비용이 시간과 비례해 커진다.

Animal 클래스를 생성할 가능성

Animal 클래스는 추상화된 동물 클래스이다. 이러한 클래스는 직접 생성해서 사용하는 일이 없다.
추상화된 개념으로 동물이라고 붙어진 클래스이다. 상속 받은 클래스는 개, 고양이, 새, 소 실제 존재하지만 동물이라는 것은 추상적인 개념으로 실제 있는 것이 아니다.
누군가 실수로 Animal 생성한다면 제대로된 기능을 수행하지 않는다.

Animal 클래스 상속자가 메소드 오버라이딩 누락할 가능성

새로 Animal 상속 받을 snake 클래스를 신규로 만든다고 가정한다.
개발자가 기대하는 것은 snake 클래스에서 sound() 오버라이딩하여 "쉬이익" 소리내도록 하는 것이다. 실수로 sound() 메소드를 오버라이딩 하지 않았다면, 부모의 기능을 상속 받아 실행된다.
그렇게 되면 우리가 기대한 것과 다르게 Animal.sound() 호출된다.

이러한 문제는 추상 클래스와 추상 메소드로 실수할 여지를 막아주도록 한다.