다형성 (Polymorphism)
다형성은 하나의 객체가 여러 가지 형태를 가질 수 있는 성질입니다. 주로 부모 클래스 타입으로 자식 클래스의 인스턴스를 참조하거나, 동일한 인터페이스를 구현한 객체들이 서로 다른 동작을 수행하게 만듭니다. 이를 통해 유연한 코드 설계와 확장성을 제공합니다.
이번에는 동물을 예시로 다형성을 사용하지 않고 각각의 객체에 대한 개별적인 메서드로 사용하는 코드를 보여드릴게요.
class Dog {
public void makeSound() {
System.out.println("멍멍");
}
}
class Cat {
public void makeSound() {
System.out.println("냥냥");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
dog.makeSound(); // 멍멍
cat.makeSound(); // 냥냥
}
}
생각해 보면 강아지나 고양이 모두 동물입니다. 그리고 대부분의 동물은 울음 소리를 가지고 있어요. 그런데 이렇게 코드를 작성하는 경우에는 새로운 동물이 추가될 때마다 새로운 코드를 작성해야 하고, 기존 코드를 수정해야 합니다.
이런 문제점을 동물이라는 공통 인터페이스를 정의해서 동일한 방식으로 다른 객체가 처리할 수 있도록 개선해볼게요.
interface Animal() {
void makeSound();
}
class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("멍멍");
}
}
class Cat implements Animal {
@Override
public void makeSound() {
System.out.println("냥냥");
}
}
다형성을 사용한다면 부모 클래스나 인터페이스를 통해 객체를 처리하므로, 새로운 클래스가 추가되더라도 기존 코드를 수정할 필요가 없어요. 그리고 동일한 메서드 호출 방식으로 다양한 객체를 처리할 수 있어 중복이 줄어듭니다. 또, 새로운 기능이나 객체를 추가할 때, 최소한의 수정으로 코드를 확장할 수 있습니다.
그런데 이러한 다형성에는 주의할 점이 존재합니다.
부모 클래스 타입으로 자식 클래스를 참조할 때, 다운 캐스팅이 필요할 수 있습니다. 캐스팅을 하지 않는 경우 런타임 에러를 유발할 수 있어요
Animal animal = new Dog();
Dog dog = (Dog) animal;
그리고 다형성을 남용하는 경우 코드가 지나치게 추상화되어 가독성이 떨어질 수 있습니다.