이번에는 배열과 for
문을 사용해서 중복을 제거해보자.
단축키
- Inline Variable :
command
+option
+N
- Extract Method :
command
+option
+M
package poly.ex2;
public class AnimalPolyMain2 {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
Caw caw = new Caw();
Animal[] animalArr = {dog, cat, caw};
// 변하지 않는 부분
for (Animal animal : animalArr) {
System.out.println("동물 소리 테스트 시작");
animal.sound();
System.out.println("동물 소리 테스트 종료");
}
}
}
배열은 같은 타입의 데이터를 나열할 수 있다.
Dog
, Cat
, Caw
는 모두 Animal
의 자식이므로 Animal
타입이다.
Animal
타입의 배열을 만들고 다형적 참조를 사용하면 된다.
// 둘은 같은 코드이다.
Animal[] animalArr = new Animal[] {dog, cat, caw};
Animal[] animalArr = {dog, cat, caw};
다형적 참조 덕분에 Dog
, Cat
, Caw
의 부모 타입인 Animal
타입으로 배열을 만들고, 각각을 배열에 포함했다.
이제 배열을 for
문을 사용해서 반복하면 된다.
// 변하지 않는 부분
for (Animal animal : animalArr) {
System.out.println("동물 소리 테스트 시작");
animal.sound();
System.out.println("동물 소리 테스트 종료");
}
animal.sound()
를 호출하지만 배열에는 Dog
, Cat
, Caw
의 인스턴스가 들어있다. 메서드 오버라이딩에 의해 각 인스턴스의 오버라이딩 된 sound()
메서드가 호출된다.
이번에는 배열과 메서드 모두 활용해서 기존 코드를 완성해보자
package poly.ex2;
public class AnimalPolyMain3 {
public static void main(String[] args) {
Animal[] animalArr = {new Dog(), new Cat(), new Caw()};
for (Animal animal : animalArr) {
soundAnimal(animal);
}
}
// 동물이 추가되어도 변하지 않는 코드
private static void soundAnimal(Animal animal) {
System.out.println("동물 소리 테스트 시작");
animal.sound();
System.out.println("동물 소리 테스트 종료");
}
}
Animal[] animalArr
를 통해서 배열을 사용한다.soundAnimal(Animal animal)
새로운 동물이 추가되어도 soundAnimal(...)
메서드는 코드 변경 없이 유지할 수 있다. 이렇게 할 수 있는 이유는 이 메서드는 Dog
, Cat
, Caw
같은 구체적인 클래스를 참조하는 것이 아니라 Animal
이라는 추상적인 부모를 참조하기 때문이다. 따라서 Animal
을 상속 받은 새로운 동물이 추가되어도 이 메서드의 코드는 변경 없이 유지할 수 있다.
여기서 잘 보면 새로운 동물이 추가되었을 때 코드가 변하는 부분과 변하지 않는 부분이 있다.
main()
은 코드가 변하는 부분이다. 새로운 동물을 생성하고 필요한 메서드를 호출한다.
soundAnimal(...)
는 코드가 변하지 않는 부분이다.
새로운 기능이 추가되었을 때 변하는 부분을 최소화 하는 것이 잘 작성된 코드이다. 이렇게 하기 위해서는 코드에서 변하는 부분과 변하지 않는 부분을 명확하게 구분 하는 것이 좋다.
Animal
클래스를 생성할 수 있는 문제Animal
클래스를 상속 받는 곳에서 sound()
메서드 오버라이딩을 하지 않을 가능성Animal
클래스는 동물이라는 클래스이다. 이 클래스를 다음과 같이 직접 생성해서 사용할 일이 있을까?
Animal animal = new Animal();
개, 고양이, 소가 실제 존재하는 것은 당연하지만, 동물이라는 추상적인 개념이 실제로 존재하는 것은 이상하다. 사실 이 클래스는 다형성을 위해서 필요한 것이지 직접 인스턴스를 생성해서 사용할 일은 없다.
하지만 Animal
도 클래스이기 때문에 인스턴스를 생성하고 사용하는데 아무런 제약이 없다. 누군가 실수로 new Animal()
을 사용해서 Animal
의 인스턴스를 생성할 수 있다는 것이다. 이렇게 생성된 인스턴스는 작동은 하지만 제대로된 기능을 수행하지는 않는다.
예를 들어서 Animal
을 상속 받은 Pig
클래스를 만든다고 가정해보자. 우리가 기대하는 것은 Pig
클래스가 sound()
메서드를 오버라이딩 해서 “꿀꿀”
이라는 소리가 나도록 하는 것이다. 그런데 개발자가 실수로 sound()
메서드를 오버라이딩 하는 것을 빠트릴 수 있다. 이렇게 되면 부모의 기능을 상속 받는다. 따라서 코드상 아무런 문제가 발생하지 않는다. 물론 프로그램을 실행하면 기대와 다르게 “꿀꿀”
이 아니라 부모 클래스에 있는 Animal.sound()
가 호출될 것이다.
출처 : 김영한의 실전 자바 - 기본편
https://www.inflearn.com/course/김영한의-실전-자바-기본편/dashboard