상속 관계와 접근 제어에 대해 알아보자.
+
: public#
: protected~
: default-
: privateprivate
: 모든 외부 호출을 막는다.default
(package-private) : 같은 패키지 안에서 호출은 허용한다.protected
: 같은 패키지 안에서 호출은 허용한다. 패키지가 달라도 상속 관계의 호출은 허용한다.public
: 모든 외부 호출을 허용한다.private
이 가장 많이 차단하고, public
이 가장 많이 허용한다.
private → default → protected → public
package extends1.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();
}
}
부모 클래스인 Parent
에는 public
, protected
, default
, private
과 같은 모든 접근 제어자가 필드와 메서드에 존재한다.
package extends1.access.child;
import extends1.access.parent.Parent;
public class Child extends Parent {
public void call() {
publicValue = 1;
protectedValue = 1; // 상속 관계 or 같은 패키지
// defaultValue = 1; // 다른 패키지 접근 불가, 컴파일 오류
// privateValue = 1; // 접근 불가, 컴파일 오류
publicMethod();
protectedMethod(); // 상속 관계 or 같은 패키지
// defaultMethod(); // 다른 패키지 접근 불가, 컴파일 오류
// privateMethod(); // 접근 불가, 컴파일 오류
printParent();
}
}
자식 클래스인 Child
에서 부모 클래스인 Parent
에 얼마나 접근할 수 있는지 확인해보자.
publicValue = 1
: 부모의 public
필드에 접근한다. public
이므로 접근할 수 있다.protectedValue = 1
: 부모의 protected
필드에 접근한다. 자식과 부모는 다른 패키지이지만, 상속 관계이므로 접근할 수 있다.defaultValue = 1
: 부모의 default
필드에 접근한다. 자식과 부모가 다른 패키지이므로 접근할 수 없다.privateValue = 1
: 부모의 private
필드에 접근한다. private
은 모든 외부 접근을 막으므로 자식이라도 호출할 수 없다.메서드의 경우도 필드와 동일하다.
package extends1.access;
import extends1.access.child.Child;
public class ExtendsAccessMain {
public static void main(String[] args) {
Child child = new Child();
child.call();
}
}
코드를 실행해보면 Child.call()
→ Parent.printParent()
순서로 호출한다.
Child
는 부모의 public
, protected
필드나 메서드만 접근할 수 있다. 반면에 Parent.printParent()
의 경우 Parent
안에 있는 메서드이기 때문에 Parent
자신의 모든 필드와 메서드에 접근할 수 있다.
본인 타입에 없으면 부모 타입에서 기능을 찾는데, 이때 접근 제어자가 영향을 준다. 왜냐하면 내부에서는 자식과 부모가 구분되어 있기 때문이다. 결국 자식 타입에서 부모 타입의 기능을 호출할 때, 부모 입장에서 보면 외부에서 호출한 것과 같다.