Java-객체지향 프로그래밍-다형성

다형성

객체지향 프로그래밍의 대표요소 중 다형성에 대해서 알아보자.
다형성이란, 하나의 객체가 여러가지 형태를 가질 수 있는 성질이다.
더 나아가, 자바에서 다형성이란 상위 클래스의 참조 변수로 하위 클래스의 참조 변수를 다루거나 동일한 이름을 가진 여러 형태의 메서드를 만들 수 있는 등 다양한 형태를 가질 수 있는 성질이다.

  • 업캐스팅, 다운캐스팅, 오버라이딩, 오버로딩등이 다형성에 속해 있다.

그럼 코드로 한번 다형성에 대해서 살펴보자.

class Main {
    public static void main(String[] args) {
        Car car = new Car();
        SmartCar smartCar = new SmartCar();
        Car smartCar2 = new SmartCar(); //업캐스팅
//        SmartCar smartCar3 = new Car();//다운캐스팅
    }
}

class Car{
    void powerOn(){
        System.out.println("시동이 걸렸습니다.");
    }
    void powerOff(){
        System.out.println("시동을 껐습니다.");
    }
}

class SmartCar extends Car{
    void autoDrive(){
        System.out.println("자율주행 모드입니다.");
    }
    void chargeBattery(){
        System.out.println("배터리를 충전합니다.");
    }
}

위의 코드는 상속에서 나타나는 대표적인 다형성이다.
일반적으로 새로운 객체를 생성할때는 동일한 클래스 타입의 참조 변수를 생성하여 사용해왔다.
하지만 만약 해당 두 객체가 상속 관계에 있다면,

Car smartCar2 = new SmartCar();

와 같이 상위 클래스 타입으로 하위 클래스 타입을 받아 초기화 할 수 있다.
이를 업캐스팅(UpCasting)이라고 한다.

SmartCar smartCar3 = new Car();

위 코드는 어떨까?
코드를 입력해보면, 알겠지만 에러를 발생할 것이다.
하위클래스에서 상위클래스로 변환하기 위해선

SmartCar smartCar3 = (SmartCar) new Car();

과 같이 상위클래스 타입을 명시해 주어야한다.(캐스팅 연산자)
이것을 다운캐스팅(DownCasting)이라고 한다.


다만, 위의 코드는 다운캐스팅 문법의 예시일뿐 실제로 해당 코드를 작성 후 실행 시 런타임 에러를 반환한다.
이유는 다운캐스팅의 경우에는 업캐스팅된 객체를 다운캐스팅하여 기존의 타입으로 변환시킬때 사용되기 때문이다.
위의 객체는 업캐스팅되어 생성된 객체가 아니므로 오류를 반환하게 된다.


그렇다면, 다형성이 왜 필요할까?

다형성을 이용하면, 타입들을 묶어서 처리할 수 있고, 타입을 묶어서 처리한다면 코드가 좀더 간결해지고 유지보수에 용이해질 것이다.

import java.util.ArrayList;

class Main {
    public static void main(String[] args) {
        ArrayList<Car> car = new ArrayList<Car>();
        car.add(new Car());
        
        ArrayList<SmartCar> smartCar = new ArrayList<SmartCar>();
        smartCar.add(new SmartCar());
        
        ArrayList<SportCar> sportCar = new ArrayList<SportCar>();
        sportCar.add(new SportCar());
    }
}

class Car{
}

class SmartCar{
}

class SportCar{
}

위의 코드는 다형성이 적용되지 않은 코드이다.
다형성이 적용되지 않았기때문에 타입에 맞는 ArrayList를 생성해주어야 하며, 개별의 관리가 필요하다.

import java.util.ArrayList;

class Main {
    public static void main(String[] args) {
        ArrayList<Car> car = new ArrayList<Car>();
        car.add(new Car());
        car.add(new SmartCar());
        car.add(new SportCar());
    }
}

class Car{
}

class SmartCar extends Car{
}

class SportCar extends Car{
}

하지만, 위와 같이 다형성을 이용하면 하나의 타입으로 묶어서 사용하고 코드가 간결해지는 편리함이 있다.

다형성의 활용법

class Main {
    public static void main(String[] args) {
        Customer customer = new Customer();
        customer.buySnack(new Bread());
        System.out.println("잔액은 " + customer.money + "원 입니다.");
    }
}

class Snack{
    int price;
    public Snack(int price){
        this.price = price;
    }
}

class Bread extends Snack{
    public Bread() {
        super(3000);
    }
    @Override
    public String toString(){return "빵";}
}
class Candy extends Snack{
    public Candy() {
        super(1500);
    }
    @Override 
    public String toString(){return "캔디";} //오버라이딩을 통한 다형성
}


class Customer{
    int money = 100000;

    void buySnack(Snack snack){ //매개변수의 다형성
        money -= snack.price;
        System.out.println(snack + "을 구입했습니다.");
    }
}

//출력
빵을 구입했습니다.
잔액은 97000 입니다.

다형성을 사용하여 매개변수와 오버라이딩 등 다양한 다형성을 적용하여 코드를 구현한 모습이다.

    void buyBread(Bread bread){ //
        money -= bread.price;
    }
    void buyCandy(Candy candy){
        money -= candy.price;
    }

만약 다형성이 없었더라면, 위와같은 메서드들을 매번 정의해주었어야 할 것이며, 지금은 두,세개정도의 메서드지만 실제 현업에서는 무수히 많은 메서드가 존재하므로… 코드 가독성은 훨씬 떨어지게 될 것이다.


정리

  • 다형성은 객체가 다양한 형태의 타입으로 변환될 수 있는 것(상속의 관계에서만)
  • 대표적으로 업캐스팅, 다운캐스팅, 메서드 오버로딩, 메서드 오버라이딩, 인터페이스, 추상메소드, 추상클래스 등이 있다.
  • 업 캐스팅시에는 형변환 되는 타입의 생략이 가능하지만, 다운캐스팅 시에는 반드시 타입앞에 캐스팅연산자(상위클래스)를 기입해야한다.