다형성(Polymorphism)의 개념과 원리
다형성은 객체지향 프로그래밍의 핵심 개념 중 하나로, 동일한 메서드 호출이 객체의 실제 타입에 따라 다르게 동작할 수 있도록 합니다. 이를 통해 다양한 객체를 일관된 방식으로 처리할 수 있으며, 코드의 유연성과 확장성을 극대화할 수 있습니다.
다형성의 구현 방식
- 컴파일 타임 다형성 : 메서드 오버로딩을 통해 구현됩니다. 메서드 이름은 동일하지만 서로 다른 매개변수를 받는 여러 메서드를 정의하며, 호출 시점에 적합한 메서드가 선택됩니다.
public class MathUtil {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
MathUtil util = new MathUtil();
System.out.println(util.add(5, 10)); // 15 출력
System.out.println(util.add(5.5, 10.5)); // 16.0 출력
}
}
- 런타임 다형성 : 메서드 오버라이딩을 통해 구현됩니다. 자식 클래스에서 부모 클래스의 메서드를 재정의해서 사용하며, 객체의 실제 타입에 따라 메서드가 호출됩니다.
public class Animal {
void makeSound() {
System.out.println("Animal sound");
}
}
public class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Bark");
}
}
public class Cat extends Animal {
@Override
void makeSound() {
System.out.println("Meow");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal;
myAnimal = new Dog();
myAnimal.makeSound(); // "Bark" 출력
myAnimal = new Cat();
myAnimal.makeSound(); // "Meow" 출력
}
}
이처럼 동일한 makeSound() 메서드가 객체의 실제 타입에 따라 다르게 동작하는 것을 런타임 다형성이라고 합니다. 이는 프로그램이 실행되는 도중에 메서드 호출이 결정되기 때문에 더 유연한 구조를 제공합니다.
다형성의 장점
- 코드의 유연성 : 부모 클래스 타입으로 자식 클래스의 객체를 다룰 수 있어, 다양한 형태의 객체를 일관된 방식으로 처리할 수 있습니다.
- 유지보수 용이성 : 새로운 기능을 추가할 때, 기존 코드를 수정하지 않고 확장할 수 있습니다.
- 확장성 : 다형성을 사용하면 새로운 클래스를 추가할 때도 기존 코드에 영향을 주지 않고 기능을 확장할 수 있습니다.
참조변수와 인스턴스의 관계
참조 변수의 형변환
부모 클래스 타입의 참조 변수가 자식 클래스의 객체를 참조할 때, 참조 변수는 부모 클래스에 정의된 멤버만 접근할 수 있습니다. 자식 클래스의 고유한 메서드나 속성에 접근하려면 형변환(Casting)이 필요합니다.
- instanceof 연산자 : 객체가 특정 클래스의 인스턴스인지 확인할 때 사용됩니다. 이를 통해 안전한 형변환을 보장할 수 있습니다.
Animal animal = new Dog();
if (animal instanceof Dog) {
Dog dog = (Dog) animal; // 형변환
dog.bark(); // Dog 클래스의 고유 메서드 호출
}
참조 변수와 다형성
자바에서는 부모 클래스 타입의 참조 변수가 자식 클래스의 인스턴스를 참조할 수 있습니다. 이를 통해 자식 클래스의 인스턴스도 부모 클래스의 일종으로 취급되며, 다형성을 구현하는 기본적인 방식입니다.
매개변수의 다형성과 배열로 객체 다루기
다형성은 메서드의 매개변수로도 활용될 수 있습니다. 부모 클래스 타입의 매개변수로 다양한 자식 클래스를 처리할 수 있기 때문에, 코드의 유연성이 매우 높아집니다. 또한, 여러 종류의 객체를 배열로 다루는 것도 다형성의 대표적인 예입니다.
class Animal {
void sound() {
System.out.println("동물이 소리를 냅니다.");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("멍멍!");
}
}
class Cat extends Animal {
@Override
void sound() {
System.out.println("야옹!");
}
}
public class Main {
public static void makeAnimalSound(Animal animal) {
animal.sound(); // 다형성을 통해 각 클래스에 맞는 메서드가 호출됨
}
public static void main(String[] args) {
makeAnimalSound(new Dog()); // "멍멍!" 출력
makeAnimalSound(new Cat()); // "야옹!" 출력
}
}
또한, 배열을 사용해서 다양한 객체를 일괄적으로 처리할 수도 있습니다. 배열에 부모 클래스 타입을 선언하면, 다양한 자식 클래스의 객체를 배열에 담을 수 있습니다.
public class Main {
public static void main(String[] args) {
Animal[] animals = {new Dog(), new Cat()};
for (Animal animal : animals) {
animal.sound(); // 각각의 동물 소리를 출력
}
}
}
이와 같이 부모 클래스 타입의 배열을 통해 자식 클래스 객체를 다룰 수 있으며, 각 객체에 맞는 동작을 수행하게 됩니다. 이는 다형성을 실용적으로 활용하는 대표적인 방식 중 하나입니다.
결론
다형성은 객체지향 프로그래밍의 핵심 원리로, 코드의 유연성과 확장성을 크게 높이는 중요한 개념입니다. 메서드 오버로딩과 오버라이딩을 통해 다형성을 구현하고, 참조 변수의 형변환과 배열을 통해 다양한 객체를 일관된 방식으로 처리할 수 있습니다. 이를 활용하면 유지보수와 확장성이 뛰어난 프로그램을 설계할 수 있습니다.
'☕️Java[자바] > OOP[객체지향프로그래밍]' 카테고리의 다른 글
[객체지향 프로그래밍] 생성자와 객체 초기화 : 효울적인 객체 생성 방법 (0) | 2024.09.22 |
---|---|
[객체지향 프로그래밍] 캡슐화 - 데이터 보호와 정보 은닉의 핵심 원리 (0) | 2024.09.20 |
[객체지향 프로그래밍] 추상화 - 복잡성을 줄이는 설계의 핵심 (1) | 2024.09.17 |
[객체지향 프로그래밍] 상속 - 코드의 재사용 (0) | 2024.09.17 |
[객체지향 프로그래밍] 절차지향을 넘어 객체지향으로! (0) | 2024.09.09 |