[캡슐화] 접근 제어자(access modifier)와 getter, setter 메서드
by mignon25접근 제어자란?
외부에서 접근 제어자가 사용된 멤버 또는 클래스로의 접근을 제한하는 역할.
캡슐화를 수행하기 위한 핵심적인 수단 중 하나
접근 제어자의 종류
| 접근 제어자 | 접근 제한 범위 |
| private | 같은 클래스 내에서만 접근 가능 |
| default | 같은 패키지 내에서만 접근 가능 |
| protected | 같은 패키지 내 + 다른 패키지의 하위 클래스에서 접근 가능 |
| public | 접근 제한 없음 |
접근 제어자가 사용될 수 있는 대상
| 대상 | 사용 가능한 접근 제어자 |
| 클래스 | public, (default) |
| 메서드 | public, protected, (default), private |
| 멤버변수 | |
| 지역변수 | X |
처음에 클래스에도 접근 제어자가 사용될 수 있다는 말에 protected 클래스는 어떻게 적용되는거지 싶어서 머리아팠는데 다행히 클래스에는 public과 default만 적용가능했다~
1. 같은 클래스인 경우
package ModifierTest.package1; // 패키지명 ModifierTest.package1
// 파일명 Parent.java
public class Parent { // Parent 클래스의 접근 제어자는 public
// a, b, c, d 에 각각 private, default, protected, public 접근 제어자 지정
private String a = "a";
String b = "b";
protected String c = "c";
public String d = "d";
public void printEach() { // 동일 클래스이기 때문에 에러 발생하지 않음
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
}
public static void main(String[] args) { // 동일 클래스이기 때문에 에러 발생하지 않음
Parent p = new Parent();
System.out.println(p.a);
System.out.println(p.b);
System.out.println(p.c);
System.out.println(p.d);
}
}
// 실행 결과
a
b
c
d
같은 클래스일 경우 a, b, c, d 에 에러 없이 모두 접근 가능하다.
동일한 패키지의 동일한 클래스 내에 있기 때문에 가장 접근 제한이 엄격한 private 변수에도 접근이 가능하다.
2. 동일한 패키지, 다른 클래스
package ModifierTest.package1; // 패키지명 ModifierTest.package1
// 파일명 Parent.java
public class Parent { // Parent 클래스의 접근 제어자는 public
// a, b, c, d 에 각각 private, default, protected, public 접근 제어자 지정
private String a = "a";
String b = "b";
protected String c = "c";
public String d = "d";
public void printEach() { // 동일 클래스이기 때문에 에러 발생하지 않음
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
}
}
package ModifierTest.package1; // 패키지명 ModifierTest.package1
// 파일명 Test.java
public class Test { // Test 클래스의 접근 제어자는 default
public static void main(String[] args) {
Parent p = new Parent();
// System.out.println(p.a); // 동일 클래스가 아니기 때문에 에러 발생!
System.out.println(p.b);
System.out.println(p.c);
System.out.println(p.d);
}
}
// 출력 결과
b
c
d
같은 패키지 내에 Test라는 이름의 클래스파일을 새로 생성해서 동일하게 실행해보면 b, c, d는 출력되지만 a에 대해서는 다음과 같은 에러가 발생한다.
java: a has private access in ModifierTest.package1.Parent
Test가 Parent의 하위클래스더라도 마찬가지로 에러가 발생한다. private 접근 제어자가 붙은 멤버에는 무조건 동일 클래스 내에서만 접근이 가능하다.
그 외의 접근제어자에 대해서는 모두 정상적으로 접근이 가능하다.
3. 다른 패키지 & 하위 클래스
package ModifierTest.package2; // package2
// 파일명 Test2.java
import ModifierTest.package1.Parent;
class Child extends Parent { // package1의 Parent 상속 (import 했으므로 패키지명 생략)
public void printEach() { // 오버라이딩
// System.out.println(a); // 에러 발생!
// System.out.println(b); // 에러 발생!
System.out.println(c);
System.out.println(d);
}
}
a(private)에 대한 에러 메세지는 위와 동일하다.
java: a has private access in ModifierTest.package1.Parent
b(default)에 대한 에러 메세지는 다음과 같다.
java: b is not public in ModifierTest.package1.Parent; cannot be accessed from outside package
default 접근제어자의 경우 무조건 같은 패키지에서만 접근 가능하다.
c(protected)의 경우 다른 패키지이지만 Parent의 하위 클래스에서의 접근이라 에러가 발생하지 않는다.
4. 다른 패키지 & 하위 클래스 X
package ModifierTest.package2; // package2
// 파일명 Test2.java
import ModifierTest.package1.Parent;
public class Test2 {
public static void main(String[] args) {
Parent p = new Parent();
// System.out.println(p.a); // 에러
// System.out.println(p.a); // 에러
// System.out.println(p.a); // 에러
System.out.println(p.a);
}
}
a(private)는 같은 클래스가 아니니까 에러, b(default)는 다른 패키지니까 에러.
그리고 이번에는 c(protected)에 대한 접근에서도 에러가 발생한다.
java: c has protected access in ModifierTest.package1.Parent
protected 접근 제어자가 사용된 멤버의 경우 이처럼 하위 클래스가 아니라면 같은 패키지 내에서만 접근이 가능하다.
접근 제어자를 왜 사용할까?
1. 외부로부터 데이터를 보호하기 위해서.
2. 외부에는 불필요한, 내부적으로만 사용되는, 부분을 감추기 위해서.
데이터가 유효한 값을 유지하도록, 또는 비밀번호와 같은 데이터를 외부에서 함부로 변경하지 못하도록 외부로부터의 접근을 제한하는 것이 필요할 수 있다. 또한 내부 작업을 위해 임시로 사용되는 멤버변수 등의 외부에서는 불필요한 데이터의 노출을 제한하여 복잡성을 줄일 수 있다.
그리고 메서드를 변경해야 한다고 가정할 때, 접근 제어자의 종류에 따라 메서드의 변경으로 인해 테스트해야 할 범위가 달라질 것이다. 그러므로 접근 제어자를 적절히 선택해서 접근 범위를 최소화하는 것이 좋다.
접근 제어자를 이용한 캡슐화 - getter, setter
접근 제어자를 통해 데이터를 효과적으로 보호하고 은닉할 수 있다.
그러나 이렇게 보호받아야 하는 데이터들도 변경이 필요한 경우가 있을 수 있다.
이런 상황에서 객체지향의 캡슐화의 목적을 달성하면서도 간접적으로 멤버변수의 값을 다룰 수 있도록 해주는 것이 바로 getter와 setter 메서드이다.
private 접근제어자를 멤버 변수에 적용하고, 해당 멤버 변수의 값을 읽고 변경할 수 있는 public 메서드를 제공하는 방식이다.
예시를 통해 살펴보자.
시간을 표시하기 위한 클래스 Time이 있다고 가정해보자.
public class Time {
private int hour;
private int minute;
private int second;
public int getHour() { return hour; }
public void setHour(int hour) {
if(hour < 0 || hour > 23) return;
this.hour = hour;
}
public int getMinute() { return minute; }
public void setMinute(int minute) {
if(minute < 0 || minute > 59) return;
this.minute = minute;
}
public int getSecond() { return second; }
public void setSecond(int second) {
if(second < 0 || second > 59) return;
this.second = second;
}
}
hour, minute, second를 멤버변수로 가진다고 할 때 이 변수들은 유효한 값의 범위가 정해져 있으므로 데이터의 유효성을 보장하기 위해 private으로 설정했고,
get으로 시작하는 메서드를 통해 외부에서 해당 멤버변수의 값을 읽을 수 있다.
외부에서 멤버 변수의 값을 변경하고자 할 때는 set으로 시작하는 메서드를 이용하면 되는데, 조건에 맞는 값일 때만 변경되므로 데이터의 유효성을 유지할 수 있다.
만약 상속을 통한 확장이 예상되는 클래스라면 접근 제한을 주되 하위 클래스에서 접근하는 것이 가능하도록 private 대신 protected를 사용한다.
'Java' 카테고리의 다른 글
| 추상화 - 추상클래스와 인터페이스 (0) | 2023.02.28 |
|---|---|
| 다형성(polymorphism) (0) | 2023.02.26 |
| 오버라이딩(overriding), super, super() (feat. this, this()) (0) | 2023.02.25 |
| Inner Class - Instance, Static, Local (0) | 2023.02.24 |
| 클래스의 상속(inheritance) (0) | 2023.02.24 |
블로그의 정보
Mignon'S Dev Log
mignon25