다형성Polymorphism

코코·2020년 7월 26일
0

Java

목록 보기
6/25

📚 자바의 정석을 정리한 내용입니다.

다형성?

운전하는 법은 한 번만 배우면 어떤 자동차든 운전할 수 있다. 자동차 브랜드나 내부 구현에 따라 달라지지 않는다. 동일한 인터페이스를 가지고 있기 때문이다. 이게 다형성이다.

OOP에서 다형성이란 여러가지 형태를 가질 수 있는 능력. 말하자면 조상클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조할 수 있는 것이다.

class Tv {
	
    boolean power;
    int channel;
    
    void power{ power = !power;}
	void channelUp() { ++channel;}
    void channelDown() { --channel;}
}

class CaptionTv extends Tv {
	String text;
    void caption() {}
}

CationTv의 인스턴스를 Tv caption = new CaptionTv() 이런 식으로 생성할 수도 있다.
다만 이럴 경우 CaptionTv가 가지고 있는 caption()는 사용할 수 없다.

참조변수 형변환

class Car {
    String color;
    int door;

    void drive() {
        System.out.println("drive, brrr....");
    }

    void stop() {
        System.out.println("stop...");
    }
}

class FireEngine extends Car {
    void water() {
        System.out.println("water...");
    }
}
public class CastingTest {
    public static void main(String[] args) {
        Car car = null;
   		//FireEngine fire = (FireEngine)new Car(); //ERROR

        FireEngine fireEngine = new FireEngine();
        FireEngine fireEngine2 = new FireEngine();
        fireEngine.water();
        

        car = fireEngine;
        //car.warter //사용 불가
        car.stop();
        fireEngine2 = (FireEngine)car;
        fireEngine2.water();
    }
}

형변환은 참조변수 타입을 변환하는 것일 뿐, 인스턴스를 변환하는 것은 아니다. 참조변수는 단지 참조하는 인스턴스에서 사용할 수 있는 멤버의 범위(개수)를 조절한다.

자손 -> 조상으로 형변환은 가능하지만
조상 -> 자손으로 형변환은 불가능하다.
왜?
실제 인스턴스인 Car의 멤버의 개수보다 FireEngine이 사용할 수 있는 멤버 개수가 더 많기 때문이다.

instanceof 연산자

참조변수가 참조하고 있는 인스턴스의 실제타입을 알아볼 때 쓴다. boolean타입으로 return하기 때문에 주로 조건문에서 유효성 검사할 때 사용한다.
instanceof의 결과가 true라는 것은 참조변수가 검사한 타입으로 형변환이 가능하다는 뜻이다.

class InstanceofTest{ 
	public static void main(String[] args) {
		MotorCyle motorCycle = new MotorCyle();
		
		if(motorCycle instanceof MotorCyle) {
			System.out.println("MotorCyle Instance");
		}
		if(motorCycle instanceof Cycle) {
			System.out.println("Cycle Instance");
		}
		if(motorCycle instanceof Object) {
			System.out.println("Object Instance");
		}			
	}
}
class Cycle {}
class MotorCyle extends Cycle{}

/* 결과
MotorCyle Instance
Cycle Instance
Object Instance
*/

MotorCycle클래스는 자기 자신인 MotorCycle타입이면서, 조상인 Cycle타입이면서, 그의 조상인 Object타입이기도하다.
이처럼 instanceof는 자신의 타입 뿐만 아니라 조상타입에도 true를 반환한다. 그러므로 MotorCycle타입은 Cycle타입으로도, Object타입으로도 형변환 할 수 있다.

참조변수가 참조하는 변수

메서드의 경우 조상 클래스의 메서드를 자손 클래스에서 오버라이딩 했다면, 참조변수 타입에 상관없이 실제 인스턴스의 메서드(오버라이딩한 메서드)가 호출된다.
하지만 멤벼변수의 경우 이름이 같을 경우 참조 변수에 따라 값이 달라진다.

class Super {
	
   int x = 10;
}
class Sub extends {

	int x = 20;
	
}

class Main {
	public static void main(String[] args) {
		Super s = new Super();
       Sub sub = new Sub();
       
       System.out.println(s.x); // 10
       System.out.println(sub.x); //20
   }
}

매개변수의 다형성

매개변수로 조상타입의 참조변수를 설정하면, 모든 자손타입의 참조변수를 매개변수로 받을 수 있다.
바로 위 예제를 참고하여, add(Super x)라는 메서드가 있다고 하자. 그럼 Super를 상속받는 Sub도 매개변수로 받을 수 있다는 뜻이다.

package com.javaex.ch7;
/*
* 가격과 포인트 점수를 정의해놓은 클래스
* 모든 상품이 가지고 있어야 할 것들을 정의한다.
* */
class Product {
    int price;
    int bonusPoint;

    /*
    * Product를 상속하는 클래스들은 조상클래스의 생성자super()를 호출할 때 인자로 가격을 입력해야 한다.
    * Product()는 입력받은 가격으로 보너스포인트를 연산해서 변수에 저장한다.
    * */
    Product(int price) {
        this.price = price;
        bonusPoint = (int)(price/10.0); //가격의 10%를 적립한다.
    }
}

class Tv extends Product {
    Tv() { 
        /*조상클래스의 생성자를 호출할 때 가격을 입력*/
        super(100); //Tv는 100만원이다.
    }
    
    //toString 오버라이딩
    @Override
    public String toString() { return "Tv"; }
}

class Computer extends Product {
    Computer() {super(200);}

    @Override
    public String toString() {return "Computer";}
}

/*
* 구매자의 정보를 정의한 클래스
* */
class Buyer {
    int money = 1000;
    int bonusPoint = 0;
    /*
    * 구매자Buyer가 상품Product를 구매하면 이 메서드를 호출한다.
    * Product Type이기 때문에 Product를 상속하는 모든 클래스를 매개변수로 받을 수 있다.(매개변수의 다형성)
    * 구매자가 가진 돈과 상품 가격을 비교(유효성검사) 후, 돈이 충분하다면
    * 1.가진 돈에서 상품 가격을 뺀다.
    * 2.보너스 점수를 계산한 다음, 구매자의 보너수점수 변수에 저장한다.
    * */
    void buy(Product product) {
        if(money < product.price) {
            System.out.println("잔액이 부족합니다.");
            return;
        }

        money -= product.price;
        bonusPoint += product.bonusPoint;
        System.out.println(product +"상품을 구매하셨습니다.");
    }
}

public class PolyArgumentTest {
    public static void main(String[] args) {
        Buyer buyer = new Buyer();

        buyer.buy(new Tv());
        buyer.buy(new Computer());

        System.out.println("남은 잔액 "+buyer.money+"만원");
        System.out.println("현재 포인트는 "+buyer.bonusPoint+"점");
    }
}

다양한 타입의 객체를 배열로

같은 클래스를 상속 받는 자손 클래스들을 묶어서 객체 배열을 만들 수 있다.
그러니까 상속 받는 클래스만 같다면, 타입이 달라도 하나의 배열로 다룰 수 있다는 것이다. 위의 예제를 약간 수정해서 배열을 만들었다.

package com.javaex.ch7;

import java.util.Arrays;

/*
* 가격과 포인트 점수를 정의해놓은 클래스
* 모든 상품이 가지고 있어야 할 것들을 정의한다.
* */
class Product {
    int price;
    int bonusPoint;

    /*
    * Product를 상속하는 클래스들은 조상클래스의 생성자super()를 호출할 때 인자로 가격을 입력해야 한다.
    * Product()는 입력받은 가격으로 보너스포인트를 연산해서 변수에 저장한다.
    * */
    Product(int price) {
        this.price = price;
        bonusPoint = (int)(price/10.0); //가격의 10%를 적립한다.
    }

    Product() {}
}

class Tv extends Product {
    Tv() { 
        /*조상클래스의 생성자를 호출할 때 가격을 입력*/
        super(100); //Tv는 100만원이다.
    }
    
    //toString 오버라이딩
    @Override
    public String toString() { return "Tv"; }
}

class Computer extends Product {
    Computer() {super(200);}

    @Override
    public String toString() {return "Computer";}
}

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

/*
* 구매자의 정보를 정의한 클래스
* */
class Buyer {
    int money = 1000;
    int bonusPoint = 0;
    //구입한 제품을 저장하기 위한 배열
    Product[] item = new Product[10]; //10개를 담을 수 있는 장바구니
    int i = 0; //배열에 사용할 index
    /*
    * 구매자Buyer가 상품Product를 구매하면 이 메서드를 호출한다.
    * Product Type이기 때문에 Product를 상속하는 모든 클래스를 매개변수로 받을 수 있다.(매개변수의 다형성)
    * 구매자가 가진 돈과 상품 가격을 비교(유효성검사) 후, 돈이 충분하다면
    * 1.가진 돈에서 상품 가격을 뺀다.
    * 2.보너스 점수를 계산한 다음, 구매자의 보너수점수 변수에 저장한다.
    * */
    void buy(Product product) {
        if(money < product.price) {
            System.out.println("잔액이 부족합니다.");
            return;
        }

        money -= product.price;
        bonusPoint += product.bonusPoint;
        item[i++] = product;
        System.out.println(product +"상품을 구매하셨습니다.");
    }
    
    /*구매한 상품 정보를 보여주는 메서드*/
    void summary() {
        int sum = 0;    //총가격
        String itemList = "";   //상품 목록

        for(int i=0; i<item.length;i++) {
            if(item[i] == null) break;

            sum += item[i].price; //상품 가격을 더한다.
            itemList += item[i] +", ";  //상품명을 추가한다.
        }
        System.out.println("구매하신 상품 목록은 " +itemList+"입니다.");
        System.out.println("구매하신 상품 총액은 " +sum+"입니다.");
    }
}

public class PolyArgumentTest {
    public static void main(String[] args) {
        Buyer buyer = new Buyer();

        buyer.buy(new Tv());
        buyer.buy(new Computer());
        buyer.buy(new Audio());

        buyer.summary();

        System.out.println("남은 잔액 "+buyer.money+"만원");
        System.out.println("현재 포인트는 "+buyer.bonusPoint+"점");

    }
}

0개의 댓글