-
Java - 상속과 다형성(오버라이딩:Overriding, 가상메서드)4카테고리 없음 2023. 11. 1. 14:18
메서드 오버라이딩
상위 클래스 메서드 재정의하기
package inheritance; public class Customer{ protected int customerID; protected String customerName; protected String customerGrade; int bonusPoint; double bonusRatio; public Customer(){ customerGrade = "SILVER" //기본등급 bonusRatio = 0.01 //보너스 포인트 기본 적립 비율 } //보너스 포인트 적립, 지불 가격 계산 메서드 piblic int calcPrice(int price){ // 보너스 포인트 계산 bonusPoin += price * bonusRatio; return price; } public String showCustomerInfo(){ return customerName + "님의 등급은 " + customerGrade + "이며, 보너스 포인트는" + bonusPoint + "입니다."; } }
앞선 게시글 <상속과 다형성(상속)1>에서 살펴봤듯이 상위 클래스 Customer에는 제품 가격을 계산하는 calcPrice()메서드가 이미 정의되어 있다. 이 메서드는 정가를 그대로 지불하도록 짜여져 있다. 하지만 VIP 고객은 정가에서 10% 할인을 받을 수 있다. 이 경우에는 생성된 VIPCustomer 클래스는 calcPrice()메서드를 그대로 사용할 수 없다. 그래서 상위 클래스에 정의한 메서드가 하위 클래스에서 구현할 내용과 맞지 않을 경우에 하위 클래스에서 이 메서드를 재정의할 수 있다. 이를 메서드 오버라이딩(method overriding)이라고 한다. 오버라이딩을 하려면 1)반환형, 2)메서드 이름, 3)매개변수 개수, 4)매개변수 자료형이 반드시 같아야 한다. 그렇지 않으면 자바 컴파일러는 재정의한 메서드를 기존 메서드와 다른 메서드로 인식한다.
VIP 고객 클래스의 제품 가격 계산 메서드 재정의하기
package inheritance; public class VIPCustomer extends Customer { private int agentID; double saleRatio; ... // 재정의한 메서드 @Override public int calcPrice(int price){ bonusPoin += price * bonusRatio; // 보너스 포인트 적립 return price - (int)(price * saleRatio); // 할인된 가격을 } ... }
위 코드에서 @Override 어노테이션은 '이 메서드는 재정의된 메서드 입니다'라고 컴파일러에 명확히 알려주는 역할을 한다.
💡 tip) 어노테이션(Annotation)이란?
어노테이션은 영어로는 주석이라는 이미이다. @ 기호와 함께 사용하며 '@어노테이션 이름'으로 표현한다. 자바에서 제공하는 어노테이션은 컴파일러에게 특정한 정보를 제공해 주는 역할을 한다. 다음 표는 주로 사용하는 표준 어노테이션이다.
어노테이션 설명 @Override 재정의된 메서드라는 정보 제공 @Functionallnterface 함수형 인터페이스라는 정보 제공 @Deprecated 이후 버전에서 사용되지 않을 수 있는 변수, 메서드에 사용됨 @SuppressWarnings 특정 경고가 나타나지 않도록함
가상 메서드
묵시적 클래스 형 변환과 메서드 재정의
... Customer vc = new VIPCustomer("10030","이진영",08); vc.calcPrice(10000); //9000원 ...
위 코드와 같을 때는 어떻게 실행될지 생각해 보자. 묵시적 형 변환에 의해 VIPCustomer 인스턴스가 생성된 뒤에 Customer형으로 변환되었다. 이때 calcPrice()는 하위 클래스에서 재정의된 메서드이며 Customer 클래스와 VIPCustomer 각각 존재한다. 이때 mian()메서드에서 vc.calcPrice(10000)를 실행하면 VIPCustomer에 재정의된 메서드를 호출하여 9,000원을 반환한다.
왜냐하면 상속에서 상위 클래스와 하위 클래스에 같은 이름의 메서드가 존재할 때 호출될 메서드의 여부는 생성된 인스턴스 형(type)에 따라 결정되기 때문이다. 다시 말해 선언한 클래스형이 아닌 생성된 인스턴스의 메서드를 호출하는 것이다. 이렇게 인스턴스의 메서드가 호출되는 기술을 '가상 메서드(virtual method)'라고 한다. 가상 메서드가 실행되는 원리를 이해하면 왜 vc.calcPrice(10000)이 Customer 클래스의 메서드가 아닌 생성된, 즉 VIPCustomer의 메서드를 호출하는지 이해할 수 있다.
가상 메서드
자바의 클래스는 멤버 변수와 메서드로 이루어져 있다. 클래스를 생성하여 인스턴스가 만들어지면 멤버 변수는 힙 메모리에 위치한다. 그렇다면 메서드는 어디에 위치할까? 변수가 사용하는 메모리와 메서드가 사용하는 메모리는 다르다. 변수는 인스턴스가 생성될 때마다 새로 생성되지만. 메서드는 실행해야 할 명령 집합이기 때문에 인스턴스가 달라도 같은 로직을 수행한다. 즉 같은 클라스의 인스턴스를 여러개 생성한다고 해서 메서드도 여러개 생성되지 않는다.
package virtualfunction; public class TestA{ int num; void aaa(){ System.out.print("aaa() 출력") } public static void main(String[] args){ TestA a1 = new TestA(); a1.aaa(); // aaa() 출력 TestA a2 = new Test(); a2.aaa(); // aaa() 출력 } }
위 코드가 실행될때 메모리의 상태를 표현하면 위 그림과 같다. main()함수가 실행되면 1)main() 함수 블록에 호출된 지역변수는 스택 메모리에 위치한다. 그리고 2)각 참조 변수 a1과 a2가 가리키는 인스턴스는 힙 메모리에 생성된다. 여기 까지는 앞선 게시물에서도 살펴본 내용이다. 메모리에서 메서드의 명령 집합은 메서드 영역(코드 영역)에 위치한다. 우리가 메서드를 호출할면 메서드 영역의 주소를 참조하여 명령이 실행된다. 따라서 인스턴스가 달라도 동일한 메서드가 호출된다.
가상메서드의 원리
일반적으로 프로그램에서 메서드를 호출한다는 것은 그 메서드의 명령 집합이 있는 메모리위치를 참조하여 명령을 실행한느 것이다. 그런데 가상 메서드의 경우에는 '가상 메서드 테이블'이 만들어진다. 가상 메서드 테이블은 각 메서드 이름과 실제 메모리 주소가 짝을 각 메서드 이름과 실제 메모리 주소가 짝을 이루고 있다. 어떤 메서드가 호출되면 이 테이블에서 주소 값을 찾아서 해당 메서드의 명령을 수행한다.
위 그림에서 알수 있듯이 calcPrice()메서드는 두 클래스에서 서로 다른 메서드 주소를 가지고 있다. 이렇게 재정의된 메서드는 실제 인스턴스에 해당하는 메서드가 호출된다. showCustomerInfo()와 같이 재정의되지 않은 메서드인 경우는 메서드 주소가 같으며 상위클래스에 정의된 메서드가 호출된다.
package inheritance; public class OverridingTest3{ public static void main(String[] args){ int price = 10000; } //Customer 인스턴스 생성 Customer customerLee = new Customer(10010,"이준희"); System.out.println(customerLee.getCustomerName()+"님이 지불해야 하는 금액은" +customerLee.calcPrice(price)+"원입니다."); //이준희 님이 지불해야 하는 금액은 10000원입니다. //VIPCustomer 인스턴스 생성 VIPCustomer customerChoi = new VIPCustomer(10020,"최준호",2345); System.out.println(customerChoi.getCustomerName()+"님이 지불해야 하는 금액은" +customerChoi.calcPrice(price)+"원입니다."); //최준호 님이 지불해야 하는 금액은 9000원입니다. //VIPCustomer 인스턴스 생성 Customer vc = new VIPCustomer(10030,"김과연",3000); System.out.println(vc.getCustomerName()+"님이 지불해야 하는 금액은" +vc.calcPrice(price)+"원입니다."); //김과연 님이 지불해야 하는 금액은 9000원입니다. }
각각 Customer형 과 VIPCustomer형 태로 선언되고 생성 된건 각각 형(type)에 맞게 가격을 반환하고, Cusotmer 형으로 선언되고 VIPCustomer형으로 선언된 인스턴스 vc는 원래 Customer형 메서드가 호출되는 것은 맞지만, 가상 메서드 방식에 의해 VIPCustomer인스턴스 메서드가 호출되어 할인 가격 9,000원이 출력된다.
변수 vc를 선언할 때 사용한 자료형(Customer)의 메서드가 호출되는 것이 아니라 생성된 인스턴스(VIPCustomer)의 메서드가 호출된다. 이를 가상메서드라고 한다. 자바의 모든 메서드는 가상 메서드이다.
[출저 - Do it! 자바 프로그래밍 입문 , 박은종]
http://www.easyspub.co.kr/20_Menu/BookView/A001/267/PUB