접근제한자, 접근제어자 (public, private, protected, default) 가 뭔데? - Java
tl;dr
public > protected > default > private
private : 속해있는 class 에서만 접근가능
default : private + 같은 패키지 안에서 접근가능
protected : default + 상속받은 클래스에도 접근가능
public : protected + 접근제한 없음
서론
접근제한자는 Java 의 기본이나 다름없지만 자세히 들여다 볼 생각하지 않고, 프로그래밍을 하였다.
상황에 맞게 사용하기 위해서 필요할 때만 찾아서 사용하다 보니 전체 구조가 틀어지고 어지럽혀지는 경험을 해본 적도 있다.
보다 더 자세히 접근제한자에 대해 알아볼 필요성을 느껴서 포스팅을 남기기로 했다.
본론
Java 의 접근제한자의 종류로는 public, private, protected 그리고 default 가 있다.
사용하는 변수나 메소드, 생성자 앞에 붙여서 해당 변수, 메소드, 생성자의 사용할 수 있는 자격을 어디까지 줄 것인지 정해 준다.
예를 들어서 설명해 보면
살고 있는 집에 도어락이 지문인식으로 되어있다고 하면, 지문등록을 내 것만 할 것인지,
가족들도 지문등록을 할 것인지 아니면 친구들이나 누구라도 들어올 수 있도록 만들어 줄 건지 하는 것이다.
이러한 접근제한자는 객체지향 ( Object-oriented Programing ) 언어 의 3대요소 캡슐화, 상속 그리고 다향성 중에서 캡슐화에 속한다.
접근제한자를 간단하게 표로 표현해 보면 다음과 같다.
제한자 \ 범위 | 같은 class | 같은 package | 상속받은 class (다른 package) |
제한없음 |
private | O | |||
default | O | O | ||
protected | O | O | O | |
public | O | O | O | O |
표에 나타낸 것과 같이 private 에서 default, protected, public 으로 갈수록 점점 제한이 풀린다는 것을 알 수 있다.
예제를 보면서 어떤 얘기인지 확인해 보도록 하자.
1. private
public class OuterJ {
private int a = 4;
private class Inner {
public Inner(){}
public int e = 5;
}
}
class SubClass extends OuterJ {
SubClass() {
int a = this.a; // access 불가
int e = this.new Inner().e; // access 불가
}
}
class Unrelated {
Unrelated(OuterJ o) {
int a = o.a; // access 불가
int e = o.new Inner().e; // access 불가
}
}
OuterJ 클래스에 private 가 붙은 변수나 클래스 모두 같은 패키지라도 접근할 수 없다는 것을 알 수 있다.
상속을 받더라도 접근할 수 없다.
이것을 통해 private 이 붙게 되면 속해있는 class 내부를 제외하고는 절대 접근할 수 없다는 것을 알 수 있다.
즉 완전 폐쇄적인 상황에서만 사용해야 한다.
2. default
public class OuterJ {
int b = 2;
static class Inner2 {
public Inner2(){}
public int f = 6;
}
}
class SubClass extends OuterJ {
SubClass() {
int b = this.b; // access 가능
int f = this.new Inner2().f; // access 가능
}
}
class Unrelated {
Unrelated(OuterJ o) {
int b = o.b; // access 가능
int f = o.new Inner2().f; // access 가능
}
}
OuterJ 클래스에 아무것도 붙지 않은 변수나 클래스를 default 라고 한다.
위의 예시에서는 동일한 패키지 내에서 default 변수랑 클래스를 호출하였기 때문에 접근이 가능하다는 것을 확인할 수 있다.
표를 보면 같은 패키지내에서는 접근이 가능하다고 되어있는데
그렇다면 외부 패키지에서 호출할 때는 어떻게 되는지 확인해보자.
class SubClassJ extends OuterJ {
SubClassJ() {
int b = this.b; // access 불가
int f = this.new Inner2().f; // access 불가
}
}
class UnrelatedJ {
UnrelatedJ(OuterJ o) {
int b = o.b; // access 불가
int f = o.new Inner2().f; // access 불가
}
}
위의 예시와 같이 외부패키지에서는 접근불가하다는 것을 알 수 있다.
이를통해 default 는 같은 패키지에서의 접근은 가능하지만 다른 패키지에서는 접근불가하다는 것을 알 수 잇다.
즉 default 는 같은 패키지내에서 사용하고 싶지만 다른 패키지에서는 사용하고 싶지 않을 때 사용하면 된다.
3. protected
public class OuterJ {
protected int c = 3;
protected class Inner3 {
public Inner3() {}
public int g = 7;
}
}
class SubClass extends OuterJ {
SubClass() {
int c = this.c; // access 가능
int g = this.new Inner3().g; // access 가능
}
}
class Unrelated {
Unrelated(OuterJ o) {
int c = o.c; // access 가능
int g = o.new Inner3().g; // access 가능
}
}
protected 같은 경우에는 같은 패키지 뿐만 아니라 다른 패키지에서도 경우에 따라 접근할 수 있다고 되어있었다.
위의 예시를 보면 같은 패키지에서는 접근할 수 있다는 것을 알 수 있다. 그렇다면 다른 패키지의 경우에는 어떻게 될까?
class SubClassJ extends OuterJ {
SubClassJ() {
int c = this.c; // access 가능
int g = this.new Inner3().g; // access 가능
}
}
class UnrelatedJ {
UnrelatedJ(OuterJ o) {
int c = o.c; // access 불가
int g = o.new Inner3().g; // access 불가
}
}
위의 예시를 확인해 보면 OuterJ 클래스를 상속받은 SubClassJ 에서는 protected 에 접근이 가능하다.
하지만 상속받지 않은 UnrelatedJ 클래스에서는 protected 에 접근할 수 없다는 것을 알 수 있다.
이를 통해 같은 패키지 뿐만 아니라 다른 패키지에서도 접근할 수 있지만 상속받았을 때만 가능하다는 제한사항이 붙는 것을 알 수 있다.
즉 protected 는 다른패키지에서 상속받을 수 있게끔 만들고 싶을 때만 사용하면 된다고 할 수 있겠다.
4. public
public class OuterJ {
public int d = 4;
public class Inner4 {
public Inner4(){}
public int h = 8;
}
}
class SubClass extends OuterJ {
SubClass() {
int d = this.d; // access 가능
int h = this.new Inner4().h; // access 가능
}
}
class Unrelated {
Unrelated(OuterJ o) {
int d = o.d; // access 가능
int h = o.new Inner4().h; // access 가능
}
}
public 의 경우에는 접근제한없이 어떤 상황이라도 접근할 수 있다.
위의 예시와 같이 같은 패키지 뿐만 아니라, 아래와 같이 다른 패키지에서도 접근이 가능하다.
class SubClassJ extends OuterJ {
SubClassJ() {
int d = this.d; // access 가능
int h = this.new Inner4().h; // access 가능
}
}
class UnrelatedJ {
UnrelatedJ(OuterJ o) {
int d = o.d; // access 가능
int h = o.new Inner4().h; // access 가능
}
}
이를 통해 같은 패키지 뿐만아니라 다른 패키지에서도 모든 상황에 대해 public 은 접근이 가능하다는 것을 알 수 있다.
즉 상황에 구애받지 않고 사용하고 싶을 때 public 을 붙여주면 된다.
마지막으로 접근제한자 사용 범위에 대해서 알아보도록 하자.
클래스 | public, default |
생성자 | public, protected, default, private |
멤버변수 | public, protected, default, private |
멤버메소드 | public, protected, default, private |
지역번수 | 접근제한자 사용 제한 (사용할 수 없음) |
결론
접근제한자에 대해 알아보았다.
비교적 어렵지 않은 내용이지만, 정확한 사용법을 알아보지 않고 사용하게 되면 코드가 중구난방식이 될 수 있기 때문에 주의 해야할 필요가 있다. 그리고 Java 의 기본이 되는 내용이니 만큼 반드시 머리속에 들어가 있어야하는 부분이다.
참조
https://www.javatpoint.com/access-modifiers
https://beginnersbook.com/2013/05/java-access-modifiers/
전체코드
// 같은 패키지 일 때
public class OuterJ {
private int a = 1;
int b = 2;
protected int c = 3;
public int d = 4;
private class Inner {
public Inner(){}
public int e = 5;
}
class Inner2 {
public Inner2() {}
public int f = 6;
}
protected class Inner3 {
public Inner3() {}
public int g = 7;
}
public class Inner4 {
public Inner4(){}
public int h = 8;
}
}
class SubClass extends OuterJ {
SubClass() {
int a = this.a; // access 불가
int b = this.b; // access 가능
int c = this.c; // access 가능
int d = this.d; // access 가능
int e = this.new Inner().e; // access 불가
int f = this.new Inner2().f; // access 가능
int g = this.new Inner3().g; // access 가능
int h = this.new Inner4().h; // access 가능
}
}
class Unrelated {
Unrelated(OuterJ o) {
int a = o.a; // access 불가
int b = o.b; // access 가능
int c = o.c; // access 가능
int d = o.d; // access 가능
int e = o.new Inner().e; // access 불가
int f = o.new Inner2().f; // access 가능
int g = o.new Inner3().g; // access 가능
int h = o.new Inner4().h; // access 가능
}
}
// 다른 패키지 일 때
class SubClassJ extends OuterJ {
SubClassJ() {
int a = this.a; // access 불가
int b = this.b; // access 불가
int c = this.c; // access 가능
int d = this.d; // access 가능
int e = this.new Inner().e; // access 불가
int f = this.new Inner2().f; // access 불가
int g = this.new Inner3().g; // access 가능
int h = this.new Inner4().h; // access 가능
}
}
class UnrelatedJ {
UnrelatedJ(OuterJ o) {
int a = o.a; // access 불가
int b = o.b; // access 불가
int c = o.c; // access 불가
int d = o.d; // access 가능
int e = o.new Inner().e; // access 불가
int f = o.new Inner2().f; // access 불가
int g = o.new Inner3().g; // access 불가
int h = o.new Inner4().h; // access 가능
}
}