[JAVA] 그래서 다형성이 대체 뭐예요?

nbh·2024년 9월 24일

JAVA

목록 보기
1/2

그래서 다형성이 대체 뭔가요?

헤드 퍼스트 자바를 읽으면서 계속 마주치는 단어, '다형성'. 무슨 느낌인지는 알겠지만 정확히 뭔지, 왜 필요한지 명확하게 설명하기는 어려웠다. 이번 기회에 다형성의 개념부터 구현 방법까지 정리해보았다.

다형성이란 무엇인가

다형성(Polymorphism)은 '여러 형태'를 의미한다. 객체지향 프로그래밍에서는 하나의 객체가 여러 타입으로 사용될 수 있는 특성을 말한다.

예를 들어, Dog 클래스가 Animal 클래스를 상속받는다면, Dog 객체는 Dog 타입으로도, Animal 타입으로도 사용할 수 있다.

Animal myAnimal = new Dog();  // Dog 객체를 Animal 타입으로 참조

이때 실제 객체는 Dog이지만, Animal 타입의 참조변수로 접근할 수 있다. 이것이 바로 다형성이다.

왜 다형성이 필요한가?

다형성이 없다면 어떻게 될까? 동물원 관리 시스템을 만든다고 생각해보자.

// 다형성을 사용하지 않을 때
public void feedAnimals(Dog dog, Cat cat, Lion lion) {
    dog.eat();
    cat.eat();
    lion.eat();
}

새로운 동물이 추가될 때마다 메서드를 수정해야 한다. 하지만 다형성을 사용하면:

// 다형성을 사용할 때
public void feedAnimals(Animal[] animals) {
    for(Animal animal : animals) {
        animal.eat();  // 실제로는 각각의 구체적인 eat() 메서드가 호출됨
    }
}

코드가 훨씬 유연해지고, 새로운 동물을 추가해도 기존 코드를 수정할 필요가 없다.

추상 클래스로 다형성 구현하기

추상 클래스는 왜 필요한가?

때로는 클래스의 인스턴스를 직접 만들면 안 되는 경우가 있다. Animal 클래스를 생각해보자. "동물"이라는 추상적 개념은 존재하지만, 구체적으로 어떤 동물인지 모르는 상태에서는 의미가 없다.

public abstract class Animal {
    protected String name;
    
    public Animal(String name) {
        this.name = name;
    }
    
    // 구체적인 메서드
    public void sleep() {
        System.out.println(name + "이 잠을 잡니다.");
    }
    
    // 추상 메서드 - 반드시 하위 클래스에서 구현해야 함
    public abstract void eat();
    public abstract void makeSound();
}

추상 메서드의 역할

추상 메서드는 하위 클래스에서 반드시 구현해야 하는 "계약"과 같다. 이를 통해 모든 동물이 eat()makeSound() 메서드를 가지도록 강제할 수 있다.

public class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }
    
    @Override
    public void eat() {
        System.out.println(name + "이 사료를 먹습니다.");
    }
    
    @Override
    public void makeSound() {
        System.out.println("멍멍!");
    }
}

public class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }
    
    @Override
    public void eat() {
        System.out.println(name + "이 생선을 먹습니다.");
    }
    
    @Override
    public void makeSound() {
        System.out.println("야옹!");
    }
}

모든 클래스의 조상, Object

자바에서 모든 클래스는 Object 클래스를 상속받는다. 명시적으로 다른 클래스를 상속하지 않으면 자동으로 Object를 상속한다.

public class MyClass {  // 자동으로 extends Object
    // ...
}

Object의 주요 메서드들

  • equals(Object obj): 객체 비교
  • hashCode(): 해시테이블에서 사용하는 해시코드
  • toString(): 객체의 문자열 표현
  • getClass(): 객체의 실제 클래스 타입

이 덕분에 모든 객체를 Object 타입으로 다룰 수 있다.

Object[] objects = {new Dog("멍이"), new Cat("야옹이"), "Hello"};
for(Object obj : objects) {
    System.out.println(obj.toString());  // 모든 객체가 toString() 메서드를 가짐
}

다운캐스팅과 instanceof

Object 타입으로 캐스팅된 객체를 원래 타입으로 사용하려면 다운캐스팅이 필요하다.

Animal myAnimal = new Dog("멍이");
if(myAnimal instanceof Dog) {
    Dog myDog = (Dog) myAnimal;  // 안전한 다운캐스팅
    // 이제 Dog의 고유 메서드 사용 가능
}

인터페이스로 다형성 확장하기

자바는 다중 상속을 지원하지 않는다. 하지만 인터페이스를 통해 다중 구현이 가능하다.

public interface Flyable {
    void fly();
}

public interface Swimmable {
    void swim();
}

public class Duck extends Animal implements Flyable, Swimmable {
    public Duck(String name) {
        super(name);
    }
    
    @Override
    public void eat() {
        System.out.println(name + "이 물풀을 먹습니다.");
    }
    
    @Override
    public void makeSound() {
        System.out.println("꽥꽥!");
    }
    
    @Override
    public void fly() {
        System.out.println(name + "이 날아갑니다.");
    }
    
    @Override
    public void swim() {
        System.out.println(name + "이 헤엄칩니다.");
    }
}

이제 Duck 객체는 Animal, Flyable, Swimmable 타입으로 모두 사용할 수 있다.

Duck duck = new Duck("도널드");
Animal animal = duck;
Flyable flyer = duck;
Swimmable swimmer = duck;

언제 무엇을 사용해야 할까?

클래스 vs 추상 클래스 vs 인터페이스

일반 클래스를 사용하는 경우:

  • 완전한 구현이 가능한 구체적인 개념
  • 인스턴스를 직접 생성해야 하는 경우

추상 클래스를 사용하는 경우:

  • 하위 클래스들이 공통으로 사용할 구현 코드가 있는 경우
  • 인스턴스 생성을 막고 싶은 경우
  • "is-a" 관계가 명확한 경우

인터페이스를 사용하는 경우:

  • 구현 코드 없이 행동만 정의하고 싶은 경우
  • 다중 구현이 필요한 경우
  • "can-do" 관계를 표현하고 싶은 경우

마무리

다형성은 객체지향 프로그래밍의 핵심 개념이다. 같은 타입으로 다양한 객체를 다룰 수 있게 해주어 코드의 유연성과 재사용성을 크게 향상시킨다. 추상 클래스와 인터페이스는 이런 다형성을 구현하는 강력한 도구들이며, 상황에 맞게 적절히 선택해서 사용하는 것이 중요하다.

다형성을 제대로 이해하고 활용하면, 변화에 유연하게 대응할 수 있는 견고한 코드를 작성할 수 있다.

0개의 댓글