IT/자바

[Effective JAVA] Item 16 public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라

진진Yang 2021. 11. 20. 16:42
반응형
class Point {
	public double x;
	public double y;
}

위와 같은 퇴보한 클래스는 데이터 필드를 직접 접근할 수 있음으로 캡슐화의 이점을 제공할 수 없다.

 

class Point {
	private double x;
    private double y;
    
    public Point(double x, double y){
    	this.x = x;
        this.y = y;
    }
    
    public double getx(){
    	return this.x;
    }
    
    public double gety(){
    	return this.y;
    }
    
    public void setx(double x){
    	this.x = x;
    }
    
    public void sety(double y){
    	this.y = y;
    }
}

이렇게 패키지 바깥에서 접근할 수 있는 클래스라면 접근자를 제공함으로써 클래스 내부 변경을 유연하게 할 수 있다.

 

package-priavte 클래스 혹은 private 중첩클래스라면 데이터 필드를 노출한다 해도 문제가 되지 않는다 

 

package-private 클래스

class Point {
	public double x;
	public double y;
    
}

해당 클래스가 package-private 접근자로 생성되었기에 같은 패키지 안에서만 값 접근 및 변경이 가능하다.

 

private 중첩 클래스

class Point {
	private double x;
	private double y;
	
    public Point (double x, double y){
    	this.x = x;
        this.y = y;
    }
    
    public void changeXY (double x, double y){
    	this.x = x;
        this.y = y;
    }
}

private 중첩 클래스임으로 같은 패키지 내에서만 해당 클래스 접근이 가능하면 값 변경 시 생성한 객체를 통해서만 접근 및 변경이 가능하다.

그래서 package-private 클래스보다 private 중첩 클래스가 더 제한적이다.

 

자바플랫폼 라이브러리에도 public 클래스의 필드를 직접 노출하지 말라는 규칙을 어긴 사례가 종종 있다.

대표적인 예로 java.awt 의 Point, Dimension 클래스이다.

public class Point extends Point2D implements java.io.Serializable {
    /**
     * The X coordinate of this <code>Point</code>.
     * If no X coordinate is set it will default to 0.
     *
     * @serial
     * @see #getLocation()
     * @see #move(int, int)
     * @since 1.0
     */
    public int x;

    /**
     * The Y coordinate of this <code>Point</code>.
     * If no Y coordinate is set it will default to 0.
     *
     * @serial
     * @see #getLocation()
     * @see #move(int, int)
     * @since 1.0
     */
    public int y;

    /*
     * JDK 1.1 serialVersionUID
     */
    private static final long serialVersionUID = -5276940640259749850L;

이렇게 public으로 클래스 필드를 선언하면 성능이슈를 이르킨다. (해당 필드 변경시 다른 작업을 할 수 없음으로)

 

불변 필드를 public으로 선언 (유효한 시간 표현을 보장하는 클래스)

public final class Time {
	private static final int HOUR_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 IllegalArqumentException("시간 : "+hour);
    	if (minute < 0 || minute >= MINUTES_PER_HOUR)
    		throw new IllegalArgumentException("분 : "+minute);
    
    	this.hour = hour;
    	this.minute = minute;
	
    }
}

또한 불변 필드를 노출하는 public 클래스는 좋을까?

불변 필드를 public으로 선언한 경우엔 위에서 언급한 단점이 조금 줄어들긴 하지만, 결코 좋은 방법은 아니다.

왜냐하면 API를 변경하지 않고서는 표현 방식을 변경할 수 없고 필드를 읽을 때 부수 작업을 할 수 없는 단점은 여전히 존재하기 때문이다.

 

 

핵심 정리

public 클래스는 절대 가변 필드를 직접 노출해서는 안 된다.

불변 필드라면 노출해도 덜 위험하지만 안심할 수는 없다 하지만 package-private 클래스나 private 중첩클래스에서는 

종종 (불변이든 가변이든)필드를 노출하는 편이 나을 때도 있다.

반응형