Java, 상속과 접근 제어자

상속 관계의 접근 제어를 위한 코드를 작성한다.
부모와 자식 패키지를 별도로 분리해주어야 한다.

접근 제어자 표현을 위해 UML 표기법 일부 사용

  • + : public
  • # : protected
  • ~ : default
  • - : private

접근 제어자 종류

  • private: 모든 외부 호출을 거부한다.
  • default (package-private): 같은 패키지 안에서 허용한다.
  • protected: 같은 패키지 안에서 허용한다. 패키지가 다른 경우 상속 관계만 허용한다.
  • public: 모든 외부 호출을 허용한다.

private 거부한 범위가 크고, public 이 허용 범위가 크다.
private -> default -> protected -> public 순으로 허용 범위가 크다.


코드 작성

다음 경로의 파일을 만들도록 한다.
parent, child 패키지가 다르다는 것을 확인하면서 코드를 작성해야 한다.

src
└access (+)
    ├Main.java
    ├parent (+)
    │ └Parent.java (+)
    └child (+)
        └Child.java (+)

Parent.java

package access.parent;

public class Parent {
    public int publicValue;
    protected int protectedValue;
    int defaultValue;
    private int privateValue;

    public void publicMethod() {
        System.out.println("Parent::publicMethod");
    }

    protected void protectedMethod() {
        System.out.println("Parent::protectedMethod");
    }

    void defaultMethod() {
        System.out.println("Parent.defaultMethod");
    }

    private void privateMethod() {
        System.out.println("Parent.privateMethod");
    }

    public void printParent() {
        System.out.println("== Parent 메소드 ==");
        System.out.println("publicValue = " + publicValue);
        System.out.println("protectedValue = " + protectedValue);
        System.out.println("defaultValue = " + defaultValue);
        System.out.println("privateValue = " + privateValue);

        // 모두 접근 가능한 부모 메소드
        defaultMethod();
        privateMethod();
        System.out.println("== Parent 메소드 끝 ==");
    }

}

Child.java

package access.child;

import access.parent.Parent;

public class Child extends Parent {

    public void call() {
        publicValue = 1; // Parent 필드를 상속받아 받아 사용할 수 있다.
        protectedValue = 1; // 상속 관계로 허용하고 있다.
        // defaultValue = 1; // 컴파일 오류 다른 패키지로 접근 불가 함
        // privateValue = 1; // 컴파일 오류, 접근 불가

        publicMethod();
        protectedMethod(); // 상속 관계로 허용 받아 메소드를 수행 가능하다.
        // defaultMethod(); // 컴파일 오류 다른 패키지로 접근 불가 함
        // privateMethod(); // 컴파일 오류, 접근 불가

        printParent();
    }
}

Main.java

package access;

import access.child.Child;

public class Main {
    public static void main(String[] args) {
        Child child = new Child();
        child.call();
    }
}

자식 클래스에서 Child 호출하면 부모 클래스의 Parent 에 얼마나 접근되었는지 확인하는 코드이다.

실행 결과

Parent::publicMethod
Parent::protectedMethod
== Parent 메소드 ==
publicValue = 1
protectedValue = 2
defaultValue = 0
privateValue = 0
Parent.defaultMethod
Parent.privateMethod
== Parent 메소드 끝 ==

접근 제어자 - 필드

  • publicValue: 필드가 public 제어자로 접근 허용하였다.
  • protectedValue: 필드가 protected 상속 관계로 접근 허용하였다.
    • 상속 관계가 아닐 시 같은 패키지에 있다면 접근을 허용된다.
  • defaultValue: 필드가 default 제어자로 접근을 허용하지 않았다.
    • 부모와 자식 패키지가 다르다.
  • privateValue: 필드가 private 제어자로 접근을 허용하지 않았다.
    • 모든 외부 접근을 차단하므로 접근할 수 없다.

접근 제어자 - 메소드

  • 메소드 호출은 Child.call() 메인에서 호출
  • 호출한 printParent() 메소드는 자신에게 없으므로 부모에게 메소드를 찾는다.
  • 상속된 부모의 메소드를 찾았으므로 printParent() 메소드를 호출한다.
  • printParent 메소드는 부모 클래스의 소유자로 private 제어자를 포함한 모든 것을 접근을 허용하고 있다.

접근 제어자의 메모리 구조

앞서 말한 printParent() 메소드 호출을 Child.call() 자식 클래스에서 호출​하는 것을 살펴보았다. 같은 메모리 공간을 사용한다 하더라도 부모 클래스랑 자식 클래스는 방을 나누어서 접근제어자가 다르다는 것을 알 수 있다.