1.  다형성  polymorphism

🚀  사용 방법은 동일하지만 실행 결과가 다양하게 나오는 성질을 말함

🚀  객체는 부품과 같아서 프로그램을 구성하는 객체를 바꾸면 프로그램의 실행 성능이 다르게 나올 수 있다.

 

  • 객체 사용 방법이 동일하다는 것은 동일한 메소드를 가지고 있다는 뜻
  • 한국 타이어와 금호 타이어는 모두 타이어를 상속하고 있으므로 부모의 메소드를 동일하게 가지고 있다고 말할 수 있다.
  • 타이어 메소드 호출 시 오버라이딩 된 메소드가 호출되는데, 오버라이딩 된 내용은 두 타이어가 다르기 때문에 실행 결과가 다르게 나옴
  • 이것을 '다형성' 이라고 하는데 다형성을 구현하기 위해서는 자동 타입 변환과 메소드 재정의가 필요하다.


1)  필드 다형성

  👾  필드 타입은 동일하지만(사용 방법은 동일하지만) 대입되는 객체가 달라져서 실행 결과가 다양하게 나올 수 있는 것을 말한다.

 

public class Car {
	//필드 선언
	public Tire tire;

	//메소드 선언
	public void run() {
		//tire 필드에 대입된 객체의 roll() 메소드 호출
		tire.roll();
	}
}
public class Tire {
	//메소드 선언
	public void roll() {
		System.out.println("회전합니다.");
	}
}

 

public class HankookTire extends Tire {
	//메소드 재정의(오버라이딩)
	@Override
	public void roll() {
		System.out.println("한국 타이어가 회전합니다.");
	}
}
public class KumhoTire extends Tire {
	//메소드 재정의(오버라이딩)
	@Override
	public void roll() {
		System.out.println("금호 타이어가 회전합니다.");
	}
}

 

public class CarExample {
	public static void main(String[] args) {
		//Car 객체 생성
		Car myCar = new Car();

		//Tire 객체 장착
		myCar.tire = new Tire();
		myCar.run();

		//HankookTire 객체 장착
		myCar.tire = new HankookTire();
		myCar.run();

		//KumhoTire 객체 장착
		myCar.tire = new KumhoTire();
		myCar.run();
	}
}

 

  • Car 클래스의 run() 메소드는 tire 필드에 대입된 객체의 roll() 메소드를 호출한다.
  • 어떤 타이어를 장착했는지에 따라 roll() 메소드의 실행 결과는 달라지게 된다. 이것이 바로 '필드의 다형성'이라고 함

 

2)  매개변수 다형성

  👾  다형성은 필드보다는 메소드를 호출할 때 많이 발생함

  👾  메소드가 클래스 타입의 매개변수를 가지고 있을 경우, 호출할 때 동일한 타입의 객체를 제공하는 것이 정석이지만 자식 객체를 제공할 수도 있다.

 

public class Driver {
    public void drive(Vehicle vehicle) {
      vehicle.run();
    }
}
  • drive() 메소드는 매개값으로 전달받은 vehicle의 run() 메소드를 호출함
Driver driver = new Driver();
Vehicle vehicle = new Vehicle();
driver.drive(vehicle);

 

  • 일반적으로 drive() 메소드를 호출한다면 위와 같이 Vehicle의 객체를 제공
  • but, 자동 타입 변환으로 인해 매개값으로 Vehicle의 자식 객체도 제공할 수 있음!

  ⚡️  drive() 메소드는 매개변수 vehicle이 참조하는 객체의 run() 메소드를 호출하는데, 자식 객체가 run() 메소드를 재정의하고 있다면 재정의된 run() 메소드가 호출된다.

      ☑️  어떤 자식 객체가 제공하느냐에 따라 drive() 실행 결과는 달라진다. 이것이 '매개변수의 다형성'

 

public class Vehicle {
	//메소드 선언
	public void run() {
		System.out.println("차량이 달립니다.");
	}
}
public class Bus extends Vehicle {
	//메소드 재정의(오버라이딩)
	@Override
	public void run() {
		System.out.println("버스가 달립니다.");
	}
}
public class Taxi extends Vehicle {
	//메소드 재정의(오버라이딩)
	@Override
	public void run() {
		System.out.println("택시가 달립니다.");
	}
}
public class Driver {
	//메소드 선언(클래스 타입의 매개변수를 가지고 있음)
	public void drive(Vehicle vehicle) {
		vehicle.run();
	}
}
public class DriverExample {
	public static void main(String[] args) {
		//Driver 객체 생성
		Driver driver = new Driver();

		//매개값으로 Bus 객체를 제공하고 driver() 메소드 호출
		Bus bus = new Bus();
		driver.drive(bus);

		//매개값으로 Taxi 객체를 제공하고 driver() 메소드 호출
		Taxi taxi = new Taxi();
		driver.drive(taxi);
	}
}

 

 

응용 예제
public class Product {
    int price; // 제품의 가격
    int bonusPoint; // 제품구매 시 제공하는 보너스 점수

    Product(int price){
        this.price = price;
        this.bonusPoint = (int)(price / 10.0); // 보너스점수는 제품가격의 10%
    }
}

 

public class Tv extends Product{
    Tv () {
        // 부모클래스의 생성자 Product(int price)를 호출한다.
        super(100); // Tv의 가격을 100만원으로 한다.
    }

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

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

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

 

1. Object 클래스 상속:
    -  모든 Java 클래스는 자동으로 Object 클래스를 상속받는다. Object 클래스는 모든 클래스의 최상위 부모 클래스이고, 이 클래스에서 제공하는 여러 메서드들이 모든 클래스에서 사용할 수 있게 된다. 그 중 하나가 toString() 메서드

2. toString() 메서드:
    -  Object 클래스에는 이미 toString() 메서드가 정의되어 있기 때문에, 어떤 클래스든 toString() 메서드를 오버라이딩할 수 있다. 따라서, Product 클래스에 따로 toString() 메서드를 정의하지 않더라도, Tv 클래스에서 이 메서드를 오버라이딩하는 것이 가능

 

public class Buyer { // 고객, 물건을 사는 사람
        int money = 1000; // 소유금액
        int bonusPoint = 0; // 보너스점수

        /* Product[] products = new Product[10]; // 구입한 제품을 저장하기 위한 배열
        int i = 0; // Product 배열에 사용될 카운터 */
        
        ArrayList<Product> products = new ArrayList<>();

        void buy (Product product){ // 부모클래스 타입으로 매개변수 받음.
            // 부모 클래스의 필드 사용. price, bonusPoint
            if (money < product.price) {
                System.out.println("잔액이 부족하여 물건을 살 수 없습니다.");
                return;
            }
            money -= product.price;  // 가진 돈에서 구입한 제품의 가격을 뺀다.
            bonusPoint += product.bonusPoint; // 제품의 보너스 점수를 추가한다.
            products.add(product); // 구입한 제품을 ArrayList에 저장한다.
            System.out.println(product + " 을/를 구입하셨습니다.");
        }
        
        void summary () { // 구매한 물품에 대한 정보를 요약해서 보여 준다.
            int sum = 0; // 구입한 물품의 가격합계
            String itemList = ""; // 구입한 물품목록

            if(products.isEmpty()) { // ArrayList가 비어있는지 확인한다.
                System.out.println("구입하신 제품이 없습니다.");
                return;
            }
            // ArrayList의 i번째에 있는 객체를 얻어 온다.
            for(int i =0; i < products.size(); i++) {
                Product product = products.get(i);
                sum += product.price;
                itemList += (i==0) ? "" + product : ", " + product;
            }


          /*  // 반복문을 이용해서 구입한 물품의 총 가격과 목록을 만든다.
            // 1) for 이용
            for (int i = 0; i < products.length; i++) {
                if (products[i] == null)
                    break;
                sum += products[i].price;
                itemList += products[i] + ", ";
            }
            // 2) foreach 사용
            // for(각 요소의 타입과 요소를 담을 변수 : 배열 또는 컬렉션)
            for (Product product : products) {
                if (product == null)
                    break;
                sum += product.price;
                itemList += product + ", ";
            }

            // 3) 반복을 줄이기 위해 인스턴스 변수 사용
            for (int i = 0; i < this.i; i++) {
                sum += products[i].price;
                itemList += products[i] + ", ";
            } */

            System.out.println("구입하신 물품의 총금액은 " + sum + "만원입니다.");
            System.out.println("구입하신 제품은 " + itemList + "입니다.");
        }

        void refund(Product product) { // 구입한 제품을 환불한다.
            if (products.remove(product)) { // 제품을 ArrayList에서 제거한다.
                money += product.price;
                bonusPoint -= product.bonusPoint;
                System.out.println(product + "을/를 반품하셨습니다.");
            } else { // 제거에 실패한 경우
                System.out.println("구입하신 제품 중 해당 제품이 없습니다.");
            }
        }
        
}
public class Test {
    /*
    코딩 순서 : Product -> Tv -> Computer -> Buyer -> Test
     */
    public static void main(String[] args) {
        Buyer b = new Buyer();
        Tv tv = new Tv();
        Computer com = new Computer();
        Audio audio = new Audio();

        b.buy(tv); // Tv 을/를 구입하셨습니다.
        b.buy(com); // Computer 을/를 구입하셨습니다.
        b.buy(audio); // Audio 을/를 구입하셨습니다.
        b.summary(); // 구입하신 물품의 총금액은 350만원입니다. 구입하신 제품은 Tv, Computer, Audio입니다.
        System.out.println(); 
        b.refund(com); // Computer을/를 반품하셨습니다.
        b.summary(); // 구입하신 물품의 총금액은 150만원입니다. 구입하신 제품은 Tv, Audio입니다.
    }
}

 

 

 

 

* 내용 참고 - 책 '이것이 자바다' 및 학원 강의 자료

+ Recent posts