java 다형성

조항주·2022년 5월 3일

study

목록 보기
7/20
post-thumbnail

다형성이란?

객체지향에서 다형성이란 '여러가지 형태를 가질 수 있는 능력'

Tv 클래스와 이를 상속받는 CaptionTv 클래스

public class Tv {
    boolean power;
    int channel;

    void power() {
        this.power = !this.power;
    }
    void channelUp() {
        ++this.channel;
    }
    void channelDown() {
        --this.channel;
    }
}
public class CaptionTv extends Tv {
    boolean caption;

    void displayCaption(String text) {
        if (this.caption) {
            System.out.println(text);
        }
    }
}

지금까지는 생성된 인스턴스를 같은 타입의 참조변수로만 사용했다

Tv tv = new Tv();
CaptionTv captionTv = new CaptionTv();

조상클래스 타입에 자손클래스 인스턴스 할당

서로 상속관계에 있는 클래스들은 조상클래스 타입의 참조변수로 자손클래스 변수의 인스턴스를 참조하는것이 가능하다

Tv tv = new CaptionTv();
CaptionTv captionTv = new CaptionTv();

뭐가 다를까
위의 코드는 둘다 CaptionTv 인스턴스를 참조하지만 Tv 타입에 담긴 인스턴스는 Tv 클래스의 멤버들만 사용할 수 있다

Tv tv = new CaptionTv();
CaptionTv captionTv = new CaptionTv();

tv.caption=true; // 컴파일 에러
captionTv.caption=true;

자손클래스 타입에 조상클래스 인스턴스 할당

반대의 경우도 가능할까

CaptionTv captionTv = new Tv(); //컴파일 에러

존재하지 않는 멤버를 사용하고자 할 가능성이 있으므로 허용하지 않는다

참조변수의 형변환

참조변수도 형변환이 가능하지만 서로 상속관계에 있는 클래스 사이에서만 가능하다

자손타입 ->조상타입 (up-casting) : 형변환 생략가능
조상타입 -> 자손타입 (Down-casting) : 형변환 생략불가

형변환은 참조변수의 타입을 변환하는 것이지 인스턴스를 변환하는 것은 아니기 때문에 참조변수의 형변환은 인스턴스에 아무런 영향을 미치지 않는다.
단지 참조변수의 형변환을 통해서 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 범위를 조절하는 것이다.

Tv tv = null;
CaptionTv captionTv1=new CaptionTv();
CaptionTv captionTv2=null;

tv=captionTv1; //업캐스팅 형변환 생략
captionTv2=(CaptionTv) tv; //다운캐스팅 형변환 생략불가

왜 자손->조상만 형변환 생략이 가능할까

자손에서 조상으로 형변환 한다면 참조변수가 다룰 수 있는 멤버의 개수가 실제 인스턴스가 갖고 있는 멤버의 개수보다 항상 적다는것이 보장되어 있으므로 문제가 되지 않는다 그래서 형변환을 생략 할 수 있다.

인스턴스 자손타입으로 형변환

Tv tv = new Tv();
CaptionTv captionTv1=null;
System.out.println(1);
captionTv1=(CaptionTv) tv;

컴파일은 성공하지만 ClassCastException 발생
캐스트 연산자를 이용해서 조상타입의 참조변수를 자손타입의 참조변수로 형변환한 것이지만 tv 참조변수가 Tv 인스턴스를 참조하고 있다

instanceof연산자

참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 instanceof 연산자를 사용한다
instanceof 연산자의 왼쪽에는 참조변수, 오른쪽에는 타입(클래스명)이 피연산자로 위치한다 반환값은 true나 false중 하나
instanceof를 이용한 연산결과로 true를 얻었다는 것은 참조변수가 검사한 타입으로 형변환이 가능하다는 것을 뜻한다.

Tv tv = new Tv();
CaptionTv captionTv = new CaptionTv();
System.out.println(tv instanceof CaptionTv); //false
System.out.println(captionTv instanceof Tv); //true

왜 다형성을 사용할까

인스턴스 타입과 일치하는 참조변수를 사용하면 인스턴스의 멤버들을 모두 사용할 수 있을 텐데 왜 조상타입의 참조변수를 사용해서 인스턴스의 일부 멤버만을 사용하도록 할까
확장성과 효율적인 프로그래밍

class Product {
    int price; // 제품 가격

    Product(int price){ // 생성자
    this.price = price;
    }
}

class Tv extends Product {
    Tv() {
        //조상 클래스의 생성자 Product(int price)를 호출.
        super(100); // Tv의 가격을 100만원으로 한다.
    }
    @Override
    public String toString() {
        //Object 클래스의 toString()을 오버라이딩한다.
        return "TV";
    }
}

class Computer extends Product {
    Computer() {
        super(200);
    }
    @Override
    public String toString() {
        return "Computer";
    }
}

class Buyer { // 고객
    int money = 1000; // 소유금액
    void buy(Product p) {
    // 매개 변수가 Product 타입의 참조변수. Product 클래스의 자손 클래스 Tv, Computer의 참조변수를 한번에 매개변수로 받아들일 수 있음.
    // 앞으로 다른 제품 클래스를 추가할 때 Product클래스를 상속받기만 하면, buy(Product  p)메서드의 매개변수로 받아들여질 수 있다.
    if(money<p.price) {
        System.out.println("잔액이 부족하여 물건을 살수 없습니다.");
        return;
    }
    money -= p.price; // 가진 돈에서 구입한 제품의 가격을 뺀다.
    }
}
public class PolyArgumentTest {
    public static void main(String[] args) {
        Buyer b = new Buyer();
        Tv tv = new Tv();
        Computer com = new Computer();

        b.buy(tv); // tv 구매
        b.buy(com); // computer 구매
    }
}

0개의 댓글