자바 리마인드#9 상속

2023. 1. 25. 18:19java일지

2023/01/24

  • 학습내용

 

1. 상속(inheritance)

 

객체 지향의 유지 보수를 편하게 하고 프로그램 수정, 추가를 유연하게 해주는 기술의 기반이 상속이다.

 

 

1) 클래스의 상속

 

 

보통 생각하기에 상속을 하는  부모 클래스에서 상속을 받는 자식 클래스로 화살표가 갈 것 같지만,

클래스 간 상속을 표현할 때는 자식 클래스에서 부모 클래스로 향한다.

 

2) 클래스 상속 문법(extends)

JAVA에서 상속을 구현할 때는 extends 예약어를 사용한다.

extends는 '연장, 확장하다' 의 의미다.

즉, 부모 클래스의 속성이나 기능을 추가로 확장하여 자식 클래스를 구현한다는 뜻이다.

 

class 자식 extends 부모 {
}

 

 

ex) 고객 클래스

public class Customer {

	private int customerID;			//멤버 변수
	private String customerName;
	private String customerGrade;
	int bonusPoint;
	double bonusRatio;

	public Customer() {			//디폴트 생성자
		customerGrade = "SILVER";
		bonusRatio = 0.01;
	}

	public int calcPrice(int price) {		//보너스 포인트 메서드
		bonusPoint += price * bonusRatio;
		return price;
	}

	public String showCustomerInfo() {		//고객 정보 반환 메서드
		return customerName + "님의 등급은 " + customerGrade +
				"이며, 보너스 포인트는" + bonusPoint + "입니다";

	}
}

ex) 고객 클래스에 새로운 등급(VIP) 추가

public class VIPCustomer {

        private int customerID;			//customer 클래스와 겹치는 멤버 변수
        private String customerName;
        private String customerGrade;
        int bonusPoint;
        double bonusRatio;
        
        private int agentID; 		//VIP 고객 과련 기능을 구현할 때만 필요한 멤버 변수
        double saleRatio;
        
        public VIPCustomer() {			//디폴트 생성자
            customerGrade = "VIP";
            bonusRatio = 0.05;
            saleRatio = 0.1;
        }

        public int calcPrice(int price) {		//보너스 포인트 메서드
            bonusPoint += price * bonusRatio;
            return price -(int) (price * saleRatio);	//할인율 적용
        }
        
        public int getAgentID() {			//VIP 고객에게만 필요한 메서드
            return agentID;
        }

        public String showCustomerInfo() {		//고객 정보 반환 메서드
            return customerName + "님의 등급은 " + customerGrade + "이며, 보너스 포인트는" + bonusPoint + "입니다";
        }
    }

 

VIP 클래스를 만들고 보니 앞에서 만든 Customer 클래스와 겹치는 메서드와 멤버변수가 있다.

이렇게 중복될 때 상속을 사용한다.

VIP 클래스는 Customer 클래스를 상속받고 VIP 고객에게 필요한 추가 속성과 기능 구현을 하는 것이다.

 

 

ex) 상속 사용 VIP 클래스

 

public class VIPCustomer extends Custmoer {	
            	
        private int agentID;	
        double saleRatio;
        public VIPCustomer() {			
            customerGrade = "VIP";	//상위 클래스에서 private 변수이므로 오류 발생
            bonusRatio = 0.05;
            saleRatio = 0.1;
        }
        public int getAgentID() {
            return agentID;
        }
    }

 

 

 

3) protected

상위 클래스에서 작성한 변수나 메서드 중 외부 클래스에서 사용할 수 없지만 하위 클래스에서는 사용할 수 있도록 지정하는 예약어가 protected 이다.

상속받은 하위 클래스에서는 public처럼 사용할 수 있는 것이다.

즉, protected는 상속된 하위 클래스를 제외한 나머지 외부 클래스에서는 private과 동일한 역할이다.

 

 

 

2. 상속의 클래스 생성과 형 변환

 

하위 클래스가 생성될 때는 상위 클래스의 생성자가 먼저 호출된다.

 

 

 

1) 하위 클래스가 생성되는 과정

 

상위 클래스 : Customer / 하위 클래스 : VIP

 

그리고 상위 클래스 생성자가 호출될 때 상위 클래스의 멤버 변수가 메모리에 생성된다.

상위 클래스의 인스턴스가 먼저 생성이 되고, 하위 클래스의 인스턴스가 생성된다.
private 이라고 생성이 안되는 것이 아닌 접근만 못하는 것이다.

 

상위 클래스의 변수가 메모리에 먼저 생성되기 때문에 하위 클래스에서도 이 값들을 모두 사용할 수 있다.

 

 

2) super(부모를 부르는 예약어)

 

super 예약어는 하위 클래스에서 상위 클래스로 접근할 때 사용한다.

하위 클래스는 상위 클래스의 주소, 즉 참조 값을 알고 있다.

이 참조 값을 가지고 있는 예약어가 super 이다.

 

this가 자기 자신의 참조 값을 가지고 있는 것과 같은 개념이다.

또한 super는 상위 클래스의 생성자를 호출하는 데도 사용한다.

 

상위 클래스에 생성자 코드가 따로 없으면 super()로 상위 클래스의 디폴트 생성자가 자동으로 호출된다.

 

상위 클래스에 디폴트 생성자가 없고 매개변수가 있는 생성자만 있을 경우

super()에 매개변수를 추가하여, 매개변수가 있는 상위 클래스의 생성자를 직접 호출해야 한다.

 

상위 클래스의 멤버 변수나 메서드를 참조하는 super

상위 클래스에 선언한 멤버 변수나 메서드를 하위 클래스에서 참조할 때도 super를 사용한다.

this를 사용하여 자신의 멤버에 접근했던 것과 비슷하다.

 

 

 

3) 상위 클래스로 묵시적 클래스 형 변환

상속에서 중요한 관계가 클래스 간의 형 변환이다.

 

위의 예시를 보면 customer(상위 클래스)가 VIP(하위 클래스)보다 일반적인 개념이다.

VIP가 Customer를 상속 받았다면 VIP형이면서 customer형이기도 하다.

 

즉 VIP 클래스로 인스턴스를 생성할 때 이 인스턴스의 자료형을 Customer형으로 클래스 형 변환해서 선언할 수 있다.

Customer vc = new VIPCustomer();
선언된 클래스형 // 생성된 인스턴스의 클래스형

그와 반대로 customer(상위 클래스)로 인스턴스를 생성할 때  VIP(하위 클래스)로 선언할 수는 없다.

 

 

 

3. 메서드 오버라이딩

 

1) 상위 클래스 메서드 재정의하기

상위 클래스에 정의한 메서드가 하위 클래스에서 구현할 내용과 맞지 않을 경우에

하위 클래스에서 이 메서드를 재정의할 수 있다.

이를 메서드 오버라이딩(method overriding)이라고 한다.

 

오버라이딩을 하려면 반환형, 메서드 이름, 매개변수 개수, 매개변수 자료형이 반드시 같아야 한다.

그렇지 않으면 컴파일러가 재정의한 메서드를 기존 메서드와 다른 메서드로 인식한다.

 

 

//상위 클래스 메서드 재정의

public class VIPCustomer extends Custmoer {
        private int agentID;
        double saleRatio;
        ...
        @Override
        public int calcPrice(int price) {
            bonusPoint += price * bonusRatio;			//보너스 포인트 적립
            return price - (int)(price * saleRatio);	//할인된 가격을 계산하여 반환
        }
        ...
}

@Override 어노테이션은 이 메서드는 재정의된 메서드입니다 라고 컴파일러에 명확히 알려 주는 역할이다.

 

 

 

2) 가상 메서드

자바는 모든 메서드가 가상 메서드다.

변수가 사용하는 메모리와 메서드가 사용하는 메모리는 다르다.

변수는 인스턴스가 생성될 때마다 새로 생성되지만,

메서드는  실행해야 할 명령 집합이기 때문에 인스턴스가 달라도 같은 로직을 수행한다.

 

main() 함수가 실행되면 지역 변수는 스택 메모리에 위치한다.

그림의 참조 변수 a1과 a2가 가리키는 인스턴스는 힙 메모리에 생성된다.

 

메서드의 명령 집합은 메서드 영역에 위치한다.

개발자가 메서드를 호출하면 메서드 영역의 주소를 참조하여 명령이 실행된다.

따라서 인스턴스가 달라도 동일한 메서도가 호출되는 것이다.

 

 

3) 가상 메서드 원리

가상 메서드는 호출하면 가상 메서드 테이블이 만들어진다.

어떤 메서드가 호출되면 이 테이블에서 주소 값을 찾아서 해당 메서드의 명령을 수행한다.

재정의된 메서드는 실제 인스턴스에 해당하는 메서드가 호출된다.

showCustomerInfo()와 같이 재정의되지 않은 메서드인 경우는 메서드 주소가 같은 상위 클래스의 메서드가 호출된다.