
OOP의 4대 특성 중 하나로, 처음엔 개념이 추상적으로 느껴진다. "하나의 타입으로 여러 종류의 객체를 다룰 수 있다"는 게 핵심인데, 코드로 보면 바로 이해된다.
같은 타입의 참조 변수로 서로 다른 구현을 가진 객체를 다루는 것이다.
public class Animal {
public void sound() {
System.out.println("...");
}
}
public class Dog extends Animal {
@Override
public void sound() { System.out.println("왈왈!"); }
}
public class Cat extends Animal {
@Override
public void sound() { System.out.println("야옹~"); }
}
// 부모 타입으로 자식 객체를 참조할 수 있다
Animal dog = new Dog();
Animal cat = new Cat();
dog.sound(); // 왈왈! (Dog의 sound 실행)
cat.sound(); // 야옹~ (Cat의 sound 실행)
변수 타입은 Animal이지만, 실제 객체가 무엇이냐에 따라 다른 메서드가 실행된다. 이를 동적 바인딩(Dynamic Binding) 이라고 한다.

동물 배열에 여러 종류를 넣고 반복문으로 한 번에 처리할 수 있다.
Animal[] animals = {new Dog(), new Cat(), new Dog()};
for (Animal animal : animals) {
animal.sound(); // 각 객체에 맞는 sound() 실행
}
// 왈왈!
// 야옹~
// 왈왈!
새로운 동물 클래스(Bird 등)가 추가되어도 이 반복문 코드는 수정할 필요가 없다.
업캐스팅: 자식 → 부모 타입으로 변환. 자동으로 된다.
Dog dog = new Dog();
Animal animal = dog; // 업캐스팅 (자동)
다운캐스팅: 부모 → 자식 타입으로 변환. 명시적으로 해야 한다.
Animal animal = new Dog();
Dog dog = (Dog) animal; // 다운캐스팅 (명시적)
dog.bark(); // Dog의 메서드 사용 가능
단, 실제 객체가 해당 타입이 아니면 ClassCastException이 발생한다.
Animal animal = new Cat();
Dog dog = (Dog) animal; // ClassCastException 발생!
instanceof로 타입을 먼저 확인하면 안전하다.
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark();
}
다형성은 "같은 코드로 다양한 객체를 처리"할 수 있게 해준다. 새로운 타입이 추가되어도 기존 코드를 수정하지 않아도 되는 유연한 구조를 만드는 기반이 된다.