단순히 개, 고양이, 소 동물들의 울음 소리를 출력하는 프로그램이다.
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("동물 소리 테스트 종료");
}
}
private static void soundCow(Cow cow) {
System.out.println("동물 소리 테스트 시작");
cow.sound();
System.out.println("동물 소리 테스트 종료");
}
Caw[] cowArr = {cat, dog, caw}; //컴파일 오류 발생!
System.out.println("동물 소리 테스트 시작");
for (Caw caw : cowArr) {
cowArr.sound();
}
System.out.println("동물 소리 테스트 종료");
문제의 핵심은 바로 타입이 다르다는 점. 다형적 참조와 메서드 오버라이딩을 활용하면 Dog, Cat, Cow가 모두 같은 타입을 사용하고, 각자 자신의 메서드도 호출할 수 있다.
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("동물 소리 테스트 종료");
}
}
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("동물 소리 테스트 종료");
}
}
Animal 클래스 인스턴스를 생성할 수 있는 문제
Cat, Dog와 달리 Animal은 추상적인 개념이지 실제로 존재하지 않는다. 그러나 제약없는 클래스이기 때문에 누군가 실수로 new Animal() 을 사용하여 인스턴스를 생성할 수 있다.
클래스를 상속 받는 곳에서 sound() 메서드 오버라이딩을 하지 않을 가능성
이렇게 되면 부모의 기능을 상속 받는다. 부모 클래스에 있는 sound()가 출력되어 버린다.
-> 좋은 프로그램은 제약이 있는 프로그램이다. 추상클래스와 추상메서드를 사용하여 이런 문제를 해결할 수 있다.