[Java][Effective Java] item 16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라
2022. 9. 29. 20:40ㆍJAVA/Effective Java
1. 적절하지 않는 public 클래스
class Point{
public double x;
public double y;
}
위와 같은 Point 클래스의 x,y 데이터 필드는 접근이 가능하기 때문에 다음과 같은 문제점이 있습니다.
- 데이터 필드에 직접 접근이 가능하기 때문에 캡슐화의 이점을 갖지 못합니다
- 외부에 클래스의 멤버를 노출시키지 않을 수 있습니다
- API를 수정하지 않고는 내부 표현을 바꿀 수 없습니다
- 예를 들어 Point 인스턴스의 필드 x를 직접 접근하는 클라이언트가 있다면 Point 클래스의 필드 x를 함부로 바꾸기가 어렵습니다. 필드 x를 변경한다면 그것을 참조하는 클라이언트도 변경되어야 합니다.
- 불변식을 보장할 수 없으며, 외부에서 필드에 접근할 때 부수 작업을 수행할 수 없습니다
- 불변식을 보장할 수 없기 때문에 클라이언트가 직접 데이터 필드의 값을 변경할 수 있습니다.
- 부수작업을 수행할 수 없다는 의미는 필드를 사용하는 순간에 어떤 동작이 실행되도록 만들 수도 없다는 의미입니다. 예를 들어 외부에서 필드에 접근할 때 부수적인 작업(복사본 던지기, 파라미터 검증 등)을 할 수도 없습니다.
예를 들어 Point 클래스의 필드멤버를 private로 설정하고 getter/setter 메서드를 정의한다고 가정합니다. 이때 Point 클래스의 필드멤버 y는 1~5사이의 값으로 설정되야 한다고 가정합니다. 코드로 표현하면 다음과 같습니다.
class Point {
private double x;
private double y;
...
public void setY(double y) {
if(y < 1 || y > 5){
throw new IllegalStateException("y는 1부터 5사이의 수를 갖습니다.");
}
this.y = y;
}
}
만약 필드멤버를 public으로 설정하여 y의 값을 저장한다면 외부에서 필드를 접근할때 파라미터 검증같은 부수적인 작업을 수행할 수 없습니다.
2. 캡슐화를 적용한 클래스
기존 public 멤버 필드들의 접근자를 private로 변경하고 접근자 메서드(getter)를 적용하여 데이터를 캡슐화합니다.
class Point {
private double x;
private double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
}
- 패키지 바깥에서 접근할 수 있는 클래스라면 접근자를 제공함으로써 클래스 내부 표현 방식을 언제든 바꿀수 있는 유용성을 얻음
- 만약 설정자 메서드(setter)를 정의하지 않는다면 불변식을 보장할 수 있습니다.
- getter / setter 메서드에 부수 작업(파라미터 검증, 형식화)을 수행할 수 있습니다.
3. package-private 클래스 또는 private 중첩 클래스
다음은 private 중첩 클래스를 표현한 코드입니다.
class Outer {
private static class Point{
public double x;
public double y;
}
public String name;
public Point getPoint(){
Point point = new Point();
point.x = 5;
point.y = 3;
return point;
}
}
- Outer 클래스는 package-private 클래스로써 같은 패키지 내에서 접근이 가능합니다.
- 클라이언트 코드가 클래스 내부 표현에 묶이기는 하나, 클라이언트도 어차피 이 클래스를 포함하는 패키지 안에서만 동작하는 코드일 뿐입니다.
- 패키지 바깥 코드는 전혀 손대지 않고도 데이터 표현 방식을 변경할 수 있습니다.
- Point 클래스는 private 중첩 클래스인데 수정 범위가 더 좁아져서 이 클래스를 포함하는 외부 클래스(Outer)까지로 제한됩니다.
4. 클래스의 public 필드가 불변인 경우
다음은 public 필드이지만 불변인 클래스 정의입니다.
public class Time {
private static final int HOURS_PER_DAY = 24;
private static final int MINUTES_PER_HOUR = 60;
public final int hour;
public final int minute;
public Time(int hour, int minute) {
if(hour < 0 || hour >= HOURS_PER_DAY){
throw new IllegalStateException("시간 : " + hour);
}
if(minute < 0 || minute >= MINUTES_PER_HOUR){
throw new IllegalStateException("분 : " + minute);
}
this.hour = hour;
this.minute = minute;
}
}
- public 클래스의 필드가 불변이라면 직접 노출 시 단점이 조금 줄어들지만 좋은 생각은 아닙니다.
- API를 변경하지 않고는 내부 표현 방식을 변경할 수 없음
- 필드를 읽을 때 부수 작업을 수행할 수 없음
- 단, 값이 변하지 않게 불변식은 보장함
핵심 정리
public 클래스는 절대 가변 필드를 직접 노출해서는 안됩니다. 불변 필드라면 노출해도 덜 위험하지만 내부 표현을 쉽게 변경할 수 없습니다. 하지만 package-private 클래스나 private 중첩 클래스에서는 종종 (불변이든 가변이든) 필드를 노출하는 편이 나을 때도 있습니다.
References
source code : https://github.com/yonghwankim-dev/effective_java/tree/master/src/role16
effective java 3/E
[이펙티브 자바3판] 4장 클래스와 인터페이스
'JAVA > Effective Java' 카테고리의 다른 글
[Java][Effective Java] item 18. 상속보다는 컴포지션을 사용하라 (0) | 2022.10.10 |
---|---|
[Java][Effective Java] item 17. 변경 가능성을 최소화하라 (0) | 2022.10.05 |
[Java][Effective Java] item 14. Comparable을 구현할지 고려하라 (0) | 2022.07.06 |
[Java][Effective Java] item 12. toString을 항상 재정의하라 (0) | 2022.06.13 |
[Java][Effective Java] item 11. equals를 재정의하려거든 hashCode도 재정의하라 (0) | 2022.06.10 |