본문 바로가기
프로그래밍/Java

Java의 final을 안전하게 사용하는 방법

by Loper Lee 2022. 4. 22.

Java의 final 예약문은 조심해서 사용하자

최근 진행하고 있는 스터디에서 봤던 책의 문구 중 한 소절에 물음표를 띄우는것부터 해당 문제가 시작되었다.

다만 내부 클래스에서 외부의 변수를 사용할 때 외부 변수는 반드시 final로 선언해 줘야 한다.

// 문제의 코드
public void add(final User user) throws SQLException {
  class AddStatement implements StatementStrategy {
    ...
  }
  StatementStrategy st = new AddStatement();
  jdbcContextWithStatementStrategy(st);
}

왜 파라미터로 객체를 받을때 final로 받아야 하는가?

나는 해당 책에서 final로 파라미터를 받아야 한다고 이야기하는것은 결국 외부에서 받은 파라미터를 내부 class나 method가 이를 수정하게 허용하면 안된다는것을 의미한다고 생각한다.
때문에 final로 선언하여 파라미터를 받으라... 설명하시지만, 여기서 맹점은 객체에 final을 주더라도 원시타입이 아닌 레퍼런스 타입은 스스로 불변 객체를 만들지 않는 이상 수정될 여지가 있다는것을 말하고 싶다.

final은 레퍼런스 타입의 불변성 적용시 조심해야한다.

public class User {
  private String name;
  private int age;

  public User(String name, int age) {
    this.name = name;
    this.age = age;
  }

  public String getName() {
    return this.name;
  }
  public int getAge() {
    return this.age;
  }

  public void setName(String name) {
    this.name = name;
  }
  public void setAge(int age) {
    this.age = age;
  }
}

위 객체를 final로 선언했을때 해당 객체는 더이상 수정되지 않고, 불변성을 유지한다고 생각하는가?
정답은 틀렸다! 해당 객체는 가변객체로 설사 선언시 final로 선언한다고 하더라도 불변성을 유지하지 못한다.

final User user = new User("아무개", 20);
...
user.setName("이게 된다고?");

결과적으로 객체는 얼마든지 수정될 수 있는 가변적 상태로 남아있게 되고, 우리가 생각하던 불변성은 지켜지지 못한다.

그럼 어찌해야하는데?

해결방법은 아주 대단한게 아니다. 객체 자체를 불변 객체로 만들면 해결될 일이다.

public class User {
  // 내부 프로퍼티를 전부 final로!
  private final String name;
  private final int age;

  public User(final String name, final int age) {
    this.name = name;
    this.age = age;
  }

  public String getName() {
    return this.name;
  }
  public int getAge() {
    return this.age;
  }

  // 컴파일 에러! 삭제해줘야함
  public void setName(String name) {
    this.name = name;
  }
  public void setAge(int age) {
    this.age = age;
  }
}

객체를 불변으로 수정하므로써 이제 더이상 외부에서 객체가 수정될 위협에 걱정하지 않아도 괜찮게 되었다.
읽기 전용으로 이용할 객체는 반드시 내부 프로퍼티들을 final로 선언해 초기 값 할당 이후의 수정을 막는게 매우 중요하다.

마무리

final은 자바를 사용하며 편리하게 이용하는 키워드다. C#의 readonly와 같이 초기값이 설정된다면 덮어씌울 수 없는 읽기전용 객체로 만들고자 사용하지만 정확한 사용법을 모른채 사용하는것은 매우 위험한 일이라고 생각한다.
스터디 내에서 띄운 물음표로 인해서 당시의 설명을 좀더 기억해보고자 이 글을 적어본다.