Java 상속(1)

강서진·2023년 11월 14일
0

Java

목록 보기
11/35
post-custom-banner

강좌 Course 1. Part 4. ch1 요약

상속

자바의 거의 모든 API가 상속을 기반으로 하고 있기 때문에 상속 개념을 이해하는 것이 필수이다. 클래스가 상속 관계라는 것은 자식 클래스가 부모 클래스를 얼마든지 사용 가능하다는 것이 기본적인 컨셉이다.
이렇게 현실의 상속과 자바에서의 상속은 조금 다르다. 자바에서의 상속은 공통된 부분을 공유하는 것에 초점이 맞춰져있다. 부모 클래스가 자식 클래스에게 상속을 해주었다고 하면, 자식 클래스는 부모 클래스에게 할당된 메모리에 접근하여 기능과 동작을 사용할 수 있는 것이다. 상속 관계에서 부모는 하나이며, 자식은 여러 개일 수 있다(=*단일상속.). 만일 자식 클래스가 부모 클래스와 같은 상태변수들을 저장할 필요가 있다면, 하위 클래스를 여러 개 만드는 것보다 부모클래스 하나를 상속하여 쓰는 것이 효율적이다.
또, 자식들은 부모클래스에 새로운 기능과 동작을 추가할 수 있기 때문에 상속은 다른 말로 확장이라고도 생각할 수 있겠다.

* 자바는 하나의 부모클래스를 갖는 단일상속만 지원한다.

수평적 구조와 수직적 구조

여기 일반사원, 관리사원, 비서, 일용직 사원들에 대한 사원정보가 있다고 가정해보자. 4부류 모두 이름, 나이, 전화번호, 주소, 입사일, 근무부서 등의 정보를 가지고 있으며, 개별적으로 서로 몇가지 다른 정보를 추가로 가지고 있다.

수평적 구조

수평적 구조는 여태 작성하였던 예시 코드들처럼 한 종류의 객체에 대해 하나의 클래스를 설계하는 것을 말한다. 이런 구조는 클래스 설계는 단순할지도 모르나, 여러 단점을 가지고 있다.
먼저, 객체를 필요할 때마다 하나씩 설계하다보면 비슷한 클래스 간의 중복적인 요소가 발생할 수 있다. 새로운 요구사항이 발생하거나 하면 반복적으로 수정해야 하기 때문에 유지보수가 번거롭다. 같은 맥락에서, 확장성 역시 낮다고 할 수 있다.
이러한 단점을 보완하는 것이 수직적 구조이다.

수직적 구조

수직적 구조는 먼저 4가지 서로 다른 부류 간의 공통된 정보와 기능을 가진 사원이라는 클래스를 만들고, 이 사원 클래스를 상속하여 개별적인 일반사원, 관리사원, 비서, 일용직 사원 클래스를 설계한다.
이렇게 수직적 구조로 된 설계는 코드의 중복된 부분을 최소화하며, 공통된 부분을 한 번에 수정할 수 있어 유지보수가 용이하다. 또, 부모 클래스를 그대로 사용하여 동작을 추가할 수 있기 때문에 확장성이 높다.
계층화, 수직적 구조, 클래스와 클래스의 관계 설계 모두 다 상속(Inheritance)을 뜻한다고 보면 된다.
상속은 코드가 조금 복잡해진다는 단점이 있으나, 장점에 비하면 미미한 편이다.

메모리를 통한 상속의 이해


지난 번 강의에서는 잘 설계된 VO 클래스의 상태정보 접근제어자가 전부 private이라고 배웠다. 그러나 상속관계에서 부모클래스는 private이 아닌 protected 접근제어자를 가진다. protected는 상속관계에서 하위클래스가 상위클래스에 접근하는 것을 허용하는 접근제어자이다(상속하였더라도 다른 패키지에 있으면 접근이 불가능하다).

// 예시: 부모클래스
public class Employee{
	protected String name;
    protected int age;
    protected String phone;
    protected String empDate;
    protected String dept;
    protected boolean marriage;
    public Employee(){}
    	super();
    }
    
// 예시: 자식클래스
public class RegEmpVO extends Employee{
	public RegEmpVO (){
    	super(); // 상위클래스의 생성자를 호출한다. 
        }

자식 클래스를 설계할 때, 클래스명 뒤에

(자식클래스) extends (부모클래스)

를 작성하고, 생성자 메서드에 super()를 넣으면 부모클래스를 상속하여 사용할 수 있다. super()은 상위클래스의 생성자를 호출하는 명령어로, 생략하더라도 컴파일 과정에서 super()가 포함된다.

상속관계에서 객체 생성

상속관계에서 객체생성에 가장 중요한 것은 부모 클래스 객체가 먼저 만들어진다는 것이다.
자식 클래스 객체를 생성하면, 자식의 생성자 메서드 속 super가 부모 클래스의 생성자 메서드를 호출한다. 이렇게 부모 객체가 먼저 생성되고, 그 다음에 자식 객체가 생성된다.
부모 객체와 자식 객체의 메모리는 딱 붙어있으며, 부모의 메모리 공간까지 전부 자식 객체의 메모리공간으로 사용된다.
만일 부모객체도 또 다른 부모클래스를 상속한 자식클래스였다면 계속 super()가 호출되어 자바의 최상위 클래스인 Object를 extend 할 때까지 거슬러 올라간다.

모든 객체가 기본적으로 최상위 클래스인 Object를 상속받는 것 같긴 하다. 객체 생성을 할 때도 Object 클래스가 필요한 것 같고... 진도를 조금 더 나가야 더 자세한 내용을 다룰 것 같다.

상속관계에서 객체생성

하지만 자식이 부모의 상태정보에 마음대로 접근 가능하다는 것은 정보은닉에 위배되는 사항이다.

// 예시: 생성자 메서드
public RegEmpVO(String name, int age, String phone, String empDate, String dept, boolean marriage){
    this.name=name;
    this.age=age;
    this.phone=phone;
    this.empDate=empDate;
    this.dept=dept;
    this.marriage=marriage;
    }
}

// 예시: 생성과 동시에 초기화
public class EmployeeInitTest {
    public static void main(String[] args) {
        RegEmpVO vo = new RegEmpVO("A",30, "010-1111-1111","2023-11-14","홍보부",true);
        System.out.println(vo.toString());
    }
}

생성자 메서드를 사용한다고 해도, protected 접근제어자를 사용하고 있기 때문에 자식 객체에서 마음대로 부모 객체의 정보를 수정할 수 있는 문제점은 그대로이다(잘못된 정보가 들어갈 수 있다). 하여 부모클래스의 멤버변수들에 protected이 아닌 private을 사용하는 것이 원칙이다. 또, 자식 클래스와 부모 클래스 중 해당하는 객체 자신이 초기화를 하도록 한다.

요약)
1. 부모클래스의 멤버변수들은 protected가 아닌 private으로 하여 정보를 보호하는 것이 원칙이다.
2. 부모 클래스, 자식 클래스 각각 초기화 생성자 메서드를 만들며, 초기화를 하는 객체 자신이 직접 초기화를 하도록 한다.

부모 객체의 멤버변수들을 private으로 설정하면, 위 예시코드의 RegEmpVO의 생성자 메서드가 작동하지 않는다. 아래에서는 이를 이어서 어떻게 객체를 초기화해야 하는지 설명한다.

상속관계에서 객체 초기화

자식 클래스에서 직접 부모 클래스 객체에 접근하는 것이 정보은닉에 위배되기 때문에, 자식 클래스에서 초기화하는 것이 아닌 부모의 생성자에서 초기화하는 것이 바람직하다. 이 때 앞에서 나왔던 super()을 사용하게 된다.
super는 부모의 생성자 메서드를 호출하기 때문에 자식 클래스에서 받은 매개변수들을 super에 담으면, 부모 클래스에서 파라미터를 전달받아 직접 초기화를 할 수 있게 된다.

// 예시: Employee 클래스
public class Employee {
    private String name;
    private int age;
    private String phone;
    private String empDate;
    private String dept;
    private boolean marriage;
    public Employee(){
        super(); // 상위클래스의 생성자를 호출
    }

    public Employee(String name, int age, String phone, String empDate, String dept, boolean marriage) {
        this.name = name;
        this.age = age;
        this.phone = phone;
        this.empDate = empDate;
        this.dept = dept;
        this.marriage = marriage;
    }
}

// 예시: RegEmp 클래스
public class RegEmpVO extends Employee{
    public RegEmpVO (){
        super(); // 상위클래스의 생성자를 호출한다.
    }

    public RegEmpVO(String name, int age, String phone, String empDate, String dept, boolean marriage){
        super(name, age, phone, empDate, dept, marriage); // 부모의 생성자 호출
    }
}
post-custom-banner

0개의 댓글