우테코에서 진행하는 강의에서 거의 모든 클래스 변수와 파라미터에 final
을 붙이는 것을 보았다.
또, 사다리 미션을 진행하며 옆 크루와 이야기를 나누며 final 키워드의 유용함을 느꼈다.
따라서 final 키워드에 대해 알아보고, 필드에 final을 붙였을 때의 장점과 단점에 대해 알아보려고 한다.
클래스, 메서드, 멤버변수, 지역변수
에 적용할 수 있다.
클래스에 붙인 경우에는 상속이 불가능하다.
메서드에 붙인 경우에는 오버라이딩이 불가능하다.
변수에 붙인 경우에는 변경할 수 없는 상수가 된다.
흔히, 불변이라고 불리지만 정확히 말하면 재할당이 불가능하다.
이 말은 곧, 예측 가능한 코드가 된다.
좋은 코드의 한 가지 특징으로, 예측 가능한 코드가 있다.
public int add(int a, int b) {
return a + b;
}
라는 함수가 있다고 가정하자.
만약, 신입 개발자가 이 코드를 잘못 건드려
public int add(int a, int b) {
a += 1;
return a + b;
}
a += 1
코드를 추가한다면, 과연 add()
가 예측 가능한 코드가 될까?
정말 있을 수 없는 이야기같지만, 실제로 다른 사람의 코드를 내가 이어 받아 리팩터링하는 경우는 무조건 있을 것이다. 이때, final
키워드가 없다면 충분히 변경할 수 있지 않은가?
따라서
public int add(final int a, final int b) {
return a + b;
}
처럼 final을 통해 함수가 예측 가능한 코드가 되도록 하는 것이 중요하다.
멀티 쓰레드 환경에서, 여러 클라이언트에서 한 가지의 객체의 값을 동시에 수정할 때 thread-safe를 신경쓰는 것은 매우 중요하다.
더군다나, 우리가 개발하는 프로그램은 한 명만을 위한 프로그램이 아닐테니 자원에 대해 thread-safe 하도록 구현하는 것은 매우 중요하다.
thread-safe 하지 않은 상황을 먼저 생각해보자.
보통 2개 이상의 thread가 하나의 공유 자원에 대해 접근할 때, 1개 이상의 thread에서 값을 변경할 때 thread-safe 하지 않은 상황이 발생한다.
이때, final
키워드를 통해 애초에 값 변경을 막아버린다면, 근본적으로 thread-safe 하다.
다만 컬렉션의 경우 유의해야 한다.
public void method() {
final List<String> list = new ArrayList<>();
list.add("a"); // 값이 제대로 들어간다
list = List.of("a", "b"); // 컴파일 오류: Cannot assign a value to final variable 'list'
}
만약 위 코드에서 list에 값 변경이 불가능하게 하고 싶다면, Collections.unmodifiableCollection()
나, List.of()
를 통해 리스트를 생성하자.
List.of()
는 아래와 같은 구현체를 가진다.static <E> List<E> of(E e1) { return new ImmutableCollections.List12<>(e1); }
final 키워드가 붙은 인스턴스는 생성자를 통해 생성될 때 값을 초기화하거나, 고정된 값을 초기화 해주어야만 선언이 가능하다.
따라서 외부에서 동적으로 값을 변경할 가능성이 줄어든다.
런타임 시 JVM의 최적화를 통해 사소한 성능의 이점을 얻을 수 있다.
public void example1() {
String a = "a";
String b = "b";
System.out.println(a + b);
}
public void example2() {
final String a = "a";
final String b = "b";
System.out.println(a + b);
}
위 사진은 example1()
과 example2()
코드의 바이트 코드를 비교한 것이다.
final 키워드를 붙이지 않았을 때는 StringConcatFactory.makeConcatWithConstants
를 통해 새로운 연산이 들어가지만, final을 붙였을 때는 "ab"가 한번에 할당되는 것을 확인할 수 있다.
새로운 독립된 객체를 만들 일이 많아진다면, 이를 만들고 기존에 필요없어진 객체를 제거하는 데에 모두 비용이 들어간다.
따라서, 합당한 이유(꼭 변경해야 함)가 있다면 꼭 final
키워드를 붙이지 않아도 좋다.
단점에 비해 얻는 장점이 성능, 보안 등으로 확실하다.
따라서 위에서 언급한 것처럼 필연적으로 자주 변경되는 필드의 경우는 final을 붙이지 않는 것을 고려해볼 수 있지만, 가능한 final 키워드를 붙이는 것이 좋아보인다.