노션으로 다시 돌아갔습니다 😅

[캡슐화] 접근 제어자(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를 사용한다. 

블로그의 정보

Mignon'S Dev Log

mignon25

활동하기