자바의 다형성에 대해 알아보자

2riing·2023년 3월 1일

다형성(polymorphism)

다형성의 의미

다형성의 뜻

다형성은 OOP에서 가장 중요한 개념입니다. 여러개(poly)와 형태(morphism)의 결합어인 다형성은, 하나의 객체가 여러가지 형태를 가질 수 있는 성질입니다. 자바의 다형성을 잘 활용하면 중복되는 코드를 줄이고 편리하게 코드를 작성할 수 있습니다.

참조 변수를 선언할 때에, 부모타입(클래스)으로 자식의 인스턴스를 참조합니다. 상위 클래스 타입의 참조변수로 하위 클래스의 객체를 참조하는 것과 같은 말입니다. 그러나, 반대로 하위에서 상위의 클래스를 불러와서 참조할 수는 없습니다. 따라서, 참조변수가 사용할 수 있는 멤버의 개수는 인스턴스 멤버의 개수보다 같거나 적습니다. 참조변수의 타입이 참조변수가 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 개수를 결정하는 것입니다.

이 포스팅의 모든 예시에서는 Daugther클래스가 자식클래스로 Mom이라는 클래스를 상속받고 있다고 가정합니다.

Mom 2riing = new Daugther();

다른 언어에서의 다형성

다형성이라는 개념은 Java에서 뿐만 아니라, 객체지향 언어인 Python과 JavaScript에서도 동일하게 적용됩니다. 또한, 요즘 가장 핫하다는 Rust도 OOP로 다형성을 구현하기 위해서 Trait라는 개념을 사용합니다. 아래는 순서대로 Python, Javascript, Rust의 다형성 코드 예시입니다.

# 부모 클래스
class Animal:
    def sound(self):
        pass

# 자식 클래스 1
class Dog(Animal):
    def sound(self):
        return "멍멍"

# 자식 클래스 2
class Cat(Animal):
    def sound(self):
        return "야옹"

# 부모 클래스를 타입으로 사용하는 함수
def animal_sound(animal):
    print(animal.sound())

# 다형성을 활용한 호출
dog = Dog()
cat = Cat()

animal_sound(dog) # "멍멍" 출력
animal_sound(cat) # "야옹" 출력
// 부모 클래스
class Animal {
  sound() {}
}

// 자식 클래스 1
class Dog extends Animal {
  sound() {
    return "멍멍";
  }
}

// 자식 클래스 2
class Cat extends Animal {
  sound() {
    return "야옹";
  }
}

// 다형성을 활용한 호출
function animal_sound(animal) {
  console.log(animal.sound());
}

let dog = new Dog();
let cat = new Cat();

animal_sound(dog); // "멍멍" 출력
animal_sound(cat); // "야옹" 출력
trait Sound {
    fn make_sound(&self);
}

struct Dog;
struct Cat;

impl Sound for Dog {
    fn make_sound(&self) {
        println!("멍멍");
    }
}

impl Sound for Cat {
    fn make_sound(&self) {
        println!("야옹");
    }
}

fn main() {
    let dog = Dog;
    let cat = Cat;
    let animals: Vec<&dyn Sound> = vec![&dog, &cat];

    for animal in animals {
        animal.make_sound();
    }
}

참조변수의 타입변환(업캐스팅, 다운캐스팅)

참조변수의 타입변환이란 서로 상속관계에 있는 클래스사이에서 타입을 변환하는 것입니다. Java 뿐만 아니라 C++과 Python에서도 타입변환인 업캐스팅과 다운캐스팅을 지원합니다. 그러나 JavaScript는 프로토타입 기반 OOP이기 때문에 클래스와 상속의 개념이 없어, 업캐스팅과 다운캐스팅과 같은 개념이 없습니다. 그러나 JavaScript를 확장한 언어인 TypeScript는 클래스와 인터페이스를 이용하여 업캐스팅과 다운캐스팅을 지원합니다.

  • 업캐스팅 : 하위클래스 → 상위클래스 타입변환, 괄호 생략 가능
  • 다운캐스팅 : 상위클래스 → 하위클래스 타입변환, 괄호 명시 필요

✔ 서로 상속관계인 경우에 업캐스팅, 다운캐스팅이 가능합니다.

  • 업캐스팅 예시
Daugther 2riing = new Daugther();
Mom minji = (Mom) 2riing; // 괄호 미생략버전
Mom hani = 2riing // 괄호 생략가능
  • 다운캐스팅 예시
Mom mommy = new Mom();
Daugther jane = (Daugther) mommy; // 괄호 생략 불가능 

instanceof 연산자

instanceof 연산자는 참조변수의 타입변환, 캐스팅이 가능한 지의 여부를 true / false로 확인할 수 있는 연산자입니다.

Daugther minji = new Daugther();
System.out.println(minji instanceof Object); // ture
System.out.println(minji instanceof Daugther); // ture
System.out.println(minji instanceof Mom); // true
System.out.println(minji instanceof Car); // false

이때, Java에서 Object 클래스는 모든 클래스의 최상위 클래스로 모든 클래스는 Object 클래스를 상속받기 때문에 true를 출력합니다. 또한, 자기 자신인 Daugther 클래스와 부모클래스인 Mom클래스를 minji라는 참조변수가 사용 가능하기 때문에 true를 리턴합니다. 그러나 Car이라는 클래스는 Daugther과 서로 상속관계가 아니기 때문에 false를 리턴합니다.

3개의 댓글

comment-user-thumbnail
2023년 3월 1일

1빠

답글 달기
comment-user-thumbnail
2023년 3월 1일

정말 도움이 됐어요!

답글 달기
comment-user-thumbnail
2023년 3월 15일

예제가 넘넘 이해가 쏙쏙 되네요!

답글 달기