[Chap 8] 8.3 오버라이딩 ~ 8.6 다운 캐스팅과 instanceof
OverridingTest1.Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package inheritance; public class OverridingTest1 { public static void main(String[] args) { Customer customerLee = new Customer(10010, "이순신"); customerLee.bonusPoint = 1000; VIPCustomer customerKim = new VIPCustomer(10020, "김유신", 12345); customerKim.bonusPoint = 10000; int price = 10000; System.out.println(customerLee.getCustomerName() + " 님이 지불해야 하는 금액은 " + customerLee.calcPrice(price) + "원 입니다."); System.out.println(customerKim.getCustomerName() + " 님이 지불해야 하는 금액은 " + customerKim.calcPrice(price) + "원 입니다."); } } | cs |
그냥 Customer 객체는 10000원 지불, VIPCustomer 객체는 9000원 지불해야됨이 출력 = 오버라이딩 됨을 확인.
그렇다면, Customer vc = new VIPCustomer();이라고 한 경우, 이 때의 매서드는 상위 클래스의 매서드가 사용될까 아니면 하위 클래스의 오버라이드 된 매서드가 사용될까?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | package inheritance; public class OverridingTest2 { public static void main(String[] args) { Customer vc = new VIPCustomer(10030, "나몰라", 2000); //VIP고객 생성 vc.bonusPoint = 1000; System.out.println(vc.getCustomerName() + " 님이 지불해야 하는 금액은 " + vc.calcPrice(10000) + "원 입니다."); } } | cs |
이 코드의 출력 결과는 9000원 이라고 나왔다. 즉 재정의가 된 매서드가 사용되었다는 말이다. 선언된 클래스형이 아닌 생성된 인스턴스의 매서드를 호출하는 것이다.
가상 메서드의 원리
가상 메서드란 각 메서드 이름과 실제 메모리 주소가 짝을 이루고 있다. 그래서 상위 클래스에서의 매서드가 하위 클래스에서 오버 라이드가 되지 않을 경우는 상위 클래스의 메서드의 주소값을 사용하게 된다. 하지만 오버라이드가 된다면 상위 클래스의 메서드와는 다른 주소값을 갖게 되므로 상위 클래스형의 메서드를 따르는 것이 아닌 하위 클래스의 인스턴스를 따르는 것이다. 교재의 예제로 말하자면 Customer형의 메서드 주소를 따르는 것이 아니라 VIPCustomer 인스턴스의 메서드가 호출되는 것이다.
다형성
다형성이란 하나의 코드가 여러 자료형으로 구현되어 실행되는 것을 말합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | package polymorphism; class Animal { public void move() { System.out.println("동물이 움직입니다."); } } class Human extends Animal { public void move() { System.out.println("사람이 두 발로 걷습니다."); } } class Tiger extends Animal { public void move() { System.out.println("호랑이가 네 발로 뜁니다."); } } class Eagle extends Animal { public void move() { System.out.println("독수리가 하늘을 납니다."); } } public class AnimalTest1 { public static void main(String[] args) { AnimalTest1 aTest = new AnimalTest1(); aTest.moveAnimal(new Human()); aTest.moveAnimal(new Tiger()); aTest.moveAnimal(new Eagle()); } public void moveAnimal(Animal animal) { animal.move(); } } | cs |
이 예시에서 보면 코드 하나지만 그 안에는 Human, Tiger, Eagle 의 자료형들이 있습니다. 같은 함수를 사용했지만 출력값은 모두 달랐습니다.
다형성의 장점
Animal 클래스를 상속받아 구현하면 모든 클래스를 쉽게 관리할 수 있다. 그리고 모든 프로그래밍의 언어의 장점처럼 유지보수가 쉽다. 하나의 코드 안에서 구현이 되어있기 때문이다.
다형성의 활용
다형성이라는 것이 한 코드 내에서 여러 개의 데이터형을 만들 수 있어서 다형성인줄 알았는데, 굳이 그럴 필요는 없었다. 위의 코드 예시를 보면 aTest
라는 AnimalTest1의 변수가 있다. 메인에는 moveAimal이라는 함수가 있다. 그리고 그 함수의 매개변수는 데이터형이다. 그래서 데이터형에 따라서 Human.move, Tiger.move, Eagle.move를 인식할 수 있는 것이다. 현재 move라는 Animal 클래스의 메서드는 각 데이터형마다 다시 오버라이딩되고 있다. 이때, 가상 메서드의 원리가 사용이 되는데 그렇지 않다면 데이터형을 하나하나 비교를 통해서 이에 해당하는 메서드를 넣어주어야 할 것이다. 한마디로 위의 코드는 어떻게 다형성이 구현이 되는지에 대한 직접적인 코드 구현이었고, 앞으로 오버라이딩 된 메서드를 사용할 때, 데이터형마다 다른 메서드가 불려지는 이유는 가상 메서드의 원리 를 통해서 확인할 수 있으며, 이를 다형성 이라고 부른다고 생각하면 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | package witharraylist; import java.util.ArrayList; public class CustomerTest { public static void main(String[] args) { ArrayList<Customer> customerList = new ArrayList<Customer>(); Customer customerLee = new Customer(10010, "이순신"); Customer customerShin = new Customer(10020, "신사임당"); Customer customerHong = new GoldCustomer(10010, "홍길동"); Customer customerYoul = new GoldCustomer(10010, "이율곡"); Customer customerKim = new VIPCustomer(10010, "김유신", 12345); customerList.add(customerLee); customerList.add(customerShin); customerList.add(customerHong); customerList.add(customerYoul); customerList.add(customerKim); System.out.println("고객 정보 출력"); for (Customer c : customerList) { System.out.println(c.showCustomerInfo()); } System.out.println("할인율과 보너스 포인트 계산"); int price = 10000; for (Customer c : customerList) { int cost = c.calcPrice(price); System.out.println(c.getCustomerName() + " 님이 " + cost + "원 지불하셨습니다."); System.out.println(c.getCustomerName() + " 님의 현재 보너스 포인트는 " + c.bonusPoint + "입니다."); } } } | cs |
그래서 이 코드를 보았을 때, ArrayList에 Customer 이라는 데이터형들이 모아져 있다. 여기서 상위클래스로 묵시적 형변환 을 알 수 있다. 뭐,,,여튼 customerList
변수에 고객 정보에 맞게 저장을 한 다음에 오버라이딩된 메서드 calcPrice
를 출력시켜 보게 되면, c
에 따라서 그 인스턴스에 맞는 메서드를 출력시키는 것을 확인할 수 있다. 이거시 다형성!
instanceof 예약어
다운 캐스팅을 하기 전에 상위 클래스로 형 변환된 인스턴스의 원래 자료형을 확인해야 변환할 때 오류를 막을 수 있다. 그래서 이를 확인하기 위한 예약어가 instanceof
이다.
1 2 3 4 | Animal hAnimal = new Human(); if (hAnimal instanceof Human) { Human human = (Human)hAnimal; } | cs |
hAnimal
은 Human형으로 생성된 인스턴스이지만 Animal형으로 묵시적으로 형 변환이 진행되었다. instanceof 예약어 왼쪽에 있는 변수의 원래 인스턴스형이 오른쪽 클래스 자료형인가를 확인한다. 예를 들자면 hAnimal
변수는 원래 Human형이므로 조건문이 참이 된다. 그때 다운 캐스팅을 하기 위해서는 꼭 (Human)
이라고 명시적 으로 변환해주어야 한다.
댓글남기기