728x90
하위 클래스가 생성되는 과정을 알아야 하는 이유를 알아야 하는 이유
- 하위 클래스가 생성될 때는 상위 클래스의 생성자가 먼저 호출됩니다.
- 상속 관계에서 클래스의 생성 과정을 살펴보면 하위 클래스가 상위 클래스의 변수와 메서드를 사용할 수 있는 이유와 하위 클래스가 상위 클래스의 자료형으로 형 변환을 할 수 있는 이유를 이해할 수 있습니다.
하위 클래스가 생성되는 과정
- 상속을 받은 하위 클래스는 상위 클래스의 변수와 메서드를 사용할 수 있다고 했습니다.
- 즉 이전에 만든 CustomerTest 클래스 예제를 살펴보면, VIPCustomer 클래스로 선언한 customerKim 인스턴스는 상속받은 상위 클래스의 변수를 자기 것처럼 사용할 수 있습니다.
- 변수를 사용할 수 있다는 것은 그 변수를 저장하고 있는 메모리가 존재한다는 뜻입니다.
- 그런데 VIPCustomer 클래스의 코드를 보면 해당 변수가 존재하지 않습니다. Customer 클래스를 상속받았을 뿐입니다.
- 여기에서 우리는 상속된 하위 클래스가 생성되는 과정을 다시 생각해 볼 필요가 있습니다.
- 테스트를 하기 위해 Customer 와 VIP Customer 클래스 생성자에 출력문을 추가하겠습니다.
public class Customer {
protected int customerID;
protected String customerName;
protected String customerGrade;
int bonusPoint;
double bonusRatio;
public Customer() {
customerGrade = "SILVER";
bonusRatio = 0.01;
System.out.println("Customer( ) 생성자 호출 ");
}
...
public class VIPCustomerExtends extends Customer{
private int agentID;
double saleRatio;
public VIPCustomerExtends() {
customerGrade = "VIP"; // 상위 클래스에서 private 변수이므로 오류 발생
bonusRatio = 0.05;
saleRatio = 0.1;
System.out.println("VIPCustomerExtends( ) 생성자 호출 ");
}
...
- 위와 같이 Customer( ) 생성자와 VIPCustomer( ) 생성자에 출력문을 추가했습니다.
- 이제 CustomerTest2 클래스를 실행하여 출력 결과를 확인해 보겠습니다.
public class CustomerTest2 extends Customer {
public static void main(String[] args) {
VIPCustomerExtends customerKim = new VIPCustomerExtends();
customerKim.setCustomerID(10020);
customerKim.setCustomerName("김유신");
customerKim.bonusPoint = 10000;
System.out.println(customerKim.showCustomerInfo());
}
}
- 출력 화면을 보면 상위 클래스의 Customer( ) 생성자가 먼저 호출되고 그다음에 VIPCustoemr( ) 가 호출되는 것을 알 수 있습니다.
- 정리하면 상위 클래스를 상속받은 하위 클래스가 생성될 때는 반드시 상위 클래스의 생성자가 먼저 호출됩니다.
- 그리고 상위 클래스 생성자가 호출될 때 상위 클래스의 멤버 변수가 메모리에 생성되는 것이지요.
- 그림으로 보면 위와 같은데, 상위 클래스의 변수가 메모리에 먼저 생성되기 때문에 하위 클래스에서도 이 값들을 모두 사용할 수 있습니다.
- 앞서서 private 으로 선언했을 경우 발생했던 오류에 대해서 다시 생각해보겠습니다. 이 경우 변수는 상위 클래스 생성자를 통해 생성되었지만 private 접근제어자 때문에 하위 클래스에서 접근 불가능한 것이었습니다.
- 지금까지 하위 클래스가 생성될 때 상위 클래스가 먼저 만들어진다는 것을 배웠습니다.
- 이제 어떤 과정으로 상위 클래스가 생성되는지 살펴봅시다.
부모를 부르는 예약어, super
- super 예약어는 하위 클래스에서 상위 클래스로 접근할 때 사용합니다.
- 하위 클래스는 상위 클래스의 주소, 즉 참조 값을 알고 있습니다.
- 이 참조 값을 가지고 있는 예약어가 바로 super 입니다. (하위 클래스가 생성될 때는 상위 클래스 생성자가 먼저 호출되기 때문에 하위 클래스에는 상위 클래스의 참조 값을 가지고 있고, 이 참조 값을 가리키는 예약어가 super 이다)
- this 가 자기 자신의 참조 값을 가지고 있는 것과 같습니다.
- super 는 또한 상위 클래스의 생성자를 호출하는 데도 사용합니다. (상위 클래스의 참조 값을 가지고 있으니 당연한 결과다)
상위 클래스 생성자 호출하기
- CustomerTest2.java 예제를 보면 VIPCustomer 만 생성하였는데 Customer 상위 클래스의 생성자가 호출되며 상위 클래스도 생성된 것을 알 수 있었습니다.
- 하위 클래스 생성자만 호출했는데 상위 클래스 생성자가 호출되는 이유는 하위 클래스 생성자에서 super( ) 를 자동으로 호출하기 때문입니다.
- super( ) 를 호출하면 상위 클래스의 디폴트 생성자가 호출됩니다.
public class VIPCustomerExtends extends Customer{
private int agentID;
double saleRatio;
public VIPCustomerExtends() {
super();
customerGrade = "VIP"; // 상위 클래스에서 private 변수이므로 오류 발생
bonusRatio = 0.05;
saleRatio = 0.1;
System.out.println("VIPCustomerExtends( ) 생성자 호출 ");
}
- 하위 클래스의 디폴트 생성자는 바이트 코드로 변환된기 전에 위와 같이 코드가 자동으로 변경됩니다.
super 예약어로 매개변수가 있는 생성자 호출하기
- 이런 경우를 한번 생각해보겠습니다.
- Customer 클래스를 생성할 때 고객 ID 와 이름을 반드시 지정해야 한다고 가정합시다.
- 이런 경우에 디폴트 생성자가 아니라 새로운 생성자를 만들어서 매개변수가 있는 생성자를 직접 구현해야 합니다.
...
public Customer(int customerID, String customerName) {
this.customerID = customerID;
this.customerName = customerName;
customerGrade = "SILVER";
bonusRatio = 0.01;
System.out.println("Customer( ) 생성자 호출 ");
}
...
- 그런데 이렇게 Customer 클래스의 디폴트 생성자를 없애고 새로운 생성자를 작성하면, Customer 클래스를 상속받은 VIPCustomer 클래스에서 오류가 발생합니다.
- "Implicit super constructor Customer( ) is undefined. Must explicit invoke another constructor.
- 이 오류 메시지는 묵시적으로 호출될 디폴트 생성자 Customer( ) 가 정의되지 않았기 때문에, 반드시 명시적으로 다른 생성자를 호출해야 한다는 뜻입니다.
- Customer 클래스를 새로 생성할 때 고객 ID와 고객 이름을 반드시 지정하여 생성하기로 했으니 VIPCustomer 클래스를 생성할 때도 이 값이 필요합니다.
- VIP 고객만을 위한 상담원 ID까지 함께 지정할 수 있도록 VIPCustomer 의 생성자를 수정해보도록 하겠습니다.
public class VIPCustomerExtends extends Customer{
private int agentID;
double saleRatio;
public VIPCustomerExtends(int customerID, String customerName, int agentID) {
super(customerID, customerName);
this.agentID = agentID;
customerGrade = "VIP"; // 상위 클래스에서 private 변수이므로 오류 발생
bonusRatio = 0.05;
saleRatio = 0.1;
System.out.println("VIPCustomerExtends(int customerID, String customerName, int agentID) 생성자 호출 ");
}
- 새로운 생성자는 고객 ID, 고객 이름, 상담원 ID를 매개변수로 받습니다.
- super 예약어는 상위 클래스 생성자를 호출하는 역할을 하며, 이번에 사용한 super( ) 예약어는 매개변수 customerID, customerName 을 사용하여 상위 클래스의 디폴트 생성자를 호출하는 것이 아니라 내가 원하는 상위 클래스의 생성자를 호출하는 것을 볼 수 있습니다.
- 작동하는 과정을 다시 한번 생각해보면 다음과 같습니다.
- super( ) 를 통해 Customer(int customerID, String customerName) 라는 상위 클래스 생성자를 호출하고 코드 순서대로 상위 클래스 멤버 변수가 초기화됩니다. 그 이후 상위 클래스 생성자 호출이 끝나면 VIPCustomer 하위 클래스 생성자의 내부 코드 수행이 마무리됩니다.
public class CustomerTest2 {
public static void main(String[] args) {
VIPCustomerExtends customerKim = new VIPCustomerExtends(10020, "김유신", 2020);
customerKim.bonusPoint = 10000;
System.out.println(customerKim.showCustomerInfo());
}
}
- 실제로 코드를 수정한 후 Test 클래스도 위와 같이 수정한 후 실행해보면 다음과 같이 결과 값이 출력되는 것을 볼 수 있습니다.
- VIP 등급인 김유신 고객을 생성할 때는 상위 클래스 생성자를 먼저 호출한 후 하위 클래스 생성자의 코드 수행이 정상적으로 마무리되는 것을 확인할 수 있습니다.
상위 클래스의 멤버 변수나 메서드를 참조하는 super
- 상위 클래스에 선언한 멤버 변수나 메서드를 하위 클래스에서 참조할 때도 super 를 사용합니다.
- 예를 들어 VIPCustomer 클래스의 showVIPInfo( ) 메서드에서 상위 크래스의 showCustomerInfo( ) 메서드를 참조해 담당 상담원 아이디를 추가로 출력하려고 할 때 다음과 같이 구현할 수 있습니다.
public String showVIPInfo() {
return super.showCustomerInfo() + "담당 상담원 아이디는" + agentID + "입니다.";
}
- super 예약어는 상위 클래스의 참조 값을 가지고 있으므로 위 코드처럼 사용하면 고객 정보를 출력하는 showCustomerInfo( ) 메서드를 새로 구현하지 않고 상위 클래스의 구현 내용을 활용할 수 있습니다. (물론 여기서 super 예약어를 사용하지 않고 상위 클래스의 메서드가 잘 호출됩니다. 그 이유는 이후 오버라이딩 파트에서 다룹니다. 아무튼, 하위 클래스가 상위 클래스와 동일한 이름의 메서드를 구현하는 경우 상위 클래스의 메서드를 가리켜야 할 때는 super 예약어를 사용해야 합니다.)
상위 클래스로 묵시적 클래스 형 변환
- 상속을 공부하면서 우리가 이해해야 하는 중요한 관계가 클래스 간의 형 변환입니다.
- Customer 와 VIPCustomer 의 관계를 생각해 봅시다.
- 새념 면에서 보면 상위 클래스인 Customer 가 VIPCustomer 보다 일반적은 개념이고, 기능 면에서 보면 VIPCustomer 가 Customer 보다 기능이 더 많습니다. 왜냐하면 상속받은 클래스는 상위 클래스 기능을 모두 사용할 수 있고 추가로 더 많은 기능을 구현하기 때문입니다.
- 따라서 VIPCustomer 는 VIPCustomer 형이면서 동시에 Customer 형이기도 합니다.
- 즉 VIPCustomer 클래스로 인스턴스를 생성할 때 이 인스턴스의 자료형을 Customer 형으로 클래스 형 변환하여 선언할 수 있습니다.
- 상위 클래스가 일반적인 개념이지만, 실질적인 기능은 자식 클래스가 더 많이 가지고 있으므로 자식 클래스는 부모 클래스로 자동 형변환이 가능합니다.
- 하지만 반대로 Customer (부모)로 인스턴스를 생성할 때 VIPCustomer (자식)형으로 선언할 수는 없습니다. 상위 클래스인 Customer 가 VIPCustomer 클래스의 기능을 다 가지고 있는 것은 아니기 때문입니다.
형 변환된 vc 가 가리키는 것
- Customer vd = new VIPCustomer( ); 에서 형 변환된 vc 가 가리키는 것은 무엇일까요?
- Customer vc = new VIPCustomer( ); 문장이 실행되면 VIPCustomer 생성자가 호출되므로 클래스 변수가 위와 같이 우선 만들어집니다.
- 그런데 클래스의 자료형이 Customer 로 한정되었습니다.
- 클래스가 형 변환이 되었을 때는 선언한 클래스형에 기반하여 멤버 변수와 메서드에 접근할 수 있습니다.
- 즉 이 vc 참조 변수가 가리킬 수 있는 변수와 메서드는 Customer 클래스의 멤버뿐입니다.
- 이렇게 클래스 형 변환을 사용하는 이유는 이후 나오는 오버라이딩과 다형성에서 자세히 다룰 것입니다.
'[그린컴퓨터] Server > JAVA(객체 지향 프로그래밍)' 카테고리의 다른 글
다형성 { 다형성이란, 다형성의 장점 } (0) | 2023.05.31 |
---|---|
메서드 오버라이딩 { 상위 클래스 메서드 재정의하기, VIP 고객 클래스의 제품 가격 계산 메서드 재정의하기, 묵시적 클래스 형 변환과 메서드 재정의, 가상 메서드 } (0) | 2023.05.30 |
상속이란 { 상속이란, 클래스의 상속, 상속을 사용하여 고객 관리 프로그램 구현하기 } (0) | 2023.05.29 |
배열 응용 프로그램 { Student 클래스 구현하기, 테스트 클래스 구현 } (0) | 2023.05.25 |
ArrayList 클래스 사용하기 { 기존 배열의 단점 & ArrayList, ArrayList 클래스의 주요 메서드, ArrayList 클래스 활용 } (0) | 2023.05.25 |