Polymorphism(다형성)

상속과 함께 객체지향개념의 중요한 특징 중 하나로 여러 가지 형태를 가질 수 있는 능력을 의미한다.

자바에서는 조상 클래스 타입의 참조 변수로 자손 클래스의 인스턴스를 참조할 수 있도록 하여 다형성을 프로그램적으로 구현하였다.

class Product {

    int price;
    int bonusPoint;

    Product(int price) {
        this.price = price;
        bonusPoint = (int) (price / 10.0);
    }
}

class Tv extends Product {

    Tv() {
        super(100);
    }

    public String toString() {
        return "Tv";
    }
}

class Example {

    public static void main(String[] args) {
        Product product = new Product(100); // Product 클래스의 인스턴스 생성
        Tv tv = new Tv(); // Tv 클래스의 인스턴스 생성
//        Product product2 = new Tv(); // Tv 클래스의 인스턴스를 Product 클래스 타입의 참조변수에 저장, Tv 인스턴스의 모든 멤버 사용 불가능
//        Tv tv2 = new Product(); // 컴파일 에러
    }
}

instanceof 연산자

참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 사용한다.

instanceof 연산자의 연산결과는 boolean 타입으로 참조변수가 검사한 타입의 인스턴스를 참조하고 있으면 true, 그렇지 않으면 false를 반환한다. true를 얻었을 때는 참조변수를 해당 타입으로 형변환 할 수 있다.

class Product {

    int price;
    int bonusPoint;

    Product(int price) {
        this.price = price;
        bonusPoint = (int) (price / 10.0);
    }
}

class Tv extends Product {

    Tv() {
        super(100);
    }

    public String toString() {
        return "Tv";
    }
}

class Example {

    public static void main(String[] args) {
        Product product = new Product(100); // Product 클래스의 인스턴스 생성
        Tv tv = new Tv(); // Tv 클래스의 인스턴스 생성
        Product product2 = new Tv(); // Tv 클래스의 인스턴스를 Product 클래스 타입의 참조변수에 저장, Tv 인스턴스의 모든 멤버 사용 불가능

        System.out.println(product instanceof Product); // true
        System.out.println(product instanceof Tv); // false
        System.out.println(tv instanceof Product); // true
        System.out.println(tv instanceof Tv); // true
        System.out.println(product2 instanceof Product); // true
        System.out.println(product2 instanceof Tv); // true
    }
}

참조변수와 인스턴스 연결

조상 클래스에 선언된 멤버변수와 같은 이름의 인스턴스 변수를 자손 클래스에 중복으로 정의했을 때, 참조변수를 통해 인스턴스 변수에 접근하면 참조변수의 타입에 따라 다른 인스턴스 변수가 사용된다.

class BindingTest {

    public static void main(String[] args) {
        Parent p = new Child();
        Child c = new Child();

        System.out.println("p.x = " + p.x); // 100
        p.method(); // Child method
        System.out.println("c.x = " + c.x); // 200
        c.method(); // Child Method
    }
}

class Parent {

    int x = 100;

    void method() {
        System.out.println("Parent Method");
    }
}

class Child extends Parent {

    int x = 200;

    void method() {
        System.out.println("Child Method");
    }
}

멤버 변수가 조상 클래스와 자손 클래스에 중복으로 정의된 경우 참조변수의 타입에 따라 사용되는 인스턴스 변수가 달라진다.

  • 조상 타입의 참조변수를 사용: 조상 클래스에 선언된 멤버 변수 사용

  • 자손 타입의 참조변수를 사용: 자손 클래스에 선언된 멤버 변수 사용

  • 메서드 사용: 참조변수의 타입과 관계없이 항상 인스턴스의 타입에 정의된 메서드가 호출

class BindingTest {

    public static void main(String[] args) {
        Parent p = new Child();
        Child c = new Child();

        System.out.println("p.x = " + p.x); // 100
        p.method(); // Parent Method
        System.out.println("c.x = " + c.x); // 100
        c.method(); // Parent Method
    }
}

class Parent {

    int x = 100;

    void method() {
        System.out.println("Parent Method");
    }
}

class Child extends Parent {

}

Child 클래스에 아무런 멤버 변수나 메서드가 없는 경우 참조변수의 타입에 관계없이 조상의 멤버들을 사용한다.

class BindingTest {

    public static void main(String[] args) {
        Parent p = new Child();
        Child c = new Child();

        System.out.println("p.x = " + p.x); // 100
        p.method();
        // x=200
        // super.x=100
        // this.x=200
        System.out.println("c.x = " + c.x); // 200
        c.method();
        // x=200
        // super.x=100
        // this.x=200
    }
}

class Parent {

    int x = 100;

    void method() {
        System.out.println("Parent Method");
    }
}

class Child extends Parent {

    int x = 200;

    void method() {
        System.out.println("x=" + x);
        System.out.println("super.x=" + super.x);
        System.out.println("this.x=" + this.x);
    }
}

자손 클래스 Child 선언된 인스턴스 변수 x와 조상 클래스 Parent 선언된 인스턴스 변수 x를 구분하기 위해 super.x, this.x를 사용할 수 있다.

매개변수의 다형성

매개변수의 다형성은 메서드의 매개변수로 조상 타입의 참조변수를 사용하는 것을 말한다.

class Product {

    int price;
    int bonusPoint;

    Product(int price) {
        this.price = price;
        bonusPoint = (int) (price / 10.0);
    }
}

class Tv extends Product {

}

class Computer extends Product {

}

class Audio extends Product {

}

class Buyer {

    int money = 1000;
    int bonusPoint = 0;
    Product[] item = new Product[10];
    int i = 0;

    void buy(Computer c) {
        this.money -= c.price;
        this.bonusPoint += c.bonusPoint;
    }

    void buy(Tv t) {
        this.money -= t.price;
        this.bonusPoint += t.bonusPoint;
    }
    // ...
}

위와 같이 구매자가 구매할 수 있는 제품이 여러 종류가 있을 때, 각 제품에 대한 buy 메서드를 구현해야하는데 이를 매개변수의 다형성을 이용하면 다음과 같이 구현할 수 있다.

class Buyer {

    int money = 1000;
    int bonusPoint = 0;
    Product[] item = new Product[10];
    int i = 0;

    void buy(Product p) {
        this.money -= p.price;
        this.bonusPoint += p.bonusPoint;
    }
}

여러 종류 객체 배열로 다루기

위 예제처럼 Product 클래스가 Tv, Computer, Audio 클래스의 조상일 때 Product 타입의 참조 변수 배열로 처리할 수 있다.

class Example {

    public static void main(String[] args) {
        Product[] product = new Product[3];
        product[0] = new Tv();
        product[1] = new Computer();
        product[2] = new Audio();
    }
}

참고자료

Last updated