final 키워드의 장단점

kdkdhoho·2023년 2월 28일
0

개요

우테코에서 진행하는 강의에서 거의 모든 클래스 변수와 파라미터에 final을 붙이는 것을 보았다.
또, 사다리 미션을 진행하며 옆 크루와 이야기를 나누며 final 키워드의 유용함을 느꼈다.
따라서 final 키워드에 대해 알아보고, 필드에 final을 붙였을 때의 장점과 단점에 대해 알아보려고 한다.

final 키워드는

클래스, 메서드, 멤버변수, 지역변수에 적용할 수 있다.
클래스에 붙인 경우에는 상속이 불가능하다.
메서드에 붙인 경우에는 오버라이딩이 불가능하다.
변수에 붙인 경우에는 변경할 수 없는 상수가 된다.

흔히, 불변이라고 불리지만 정확히 말하면 재할당이 불가능하다.

장점

1. 변경 가능성을 최소화한다.

이 말은 곧, 예측 가능한 코드가 된다.

좋은 코드의 한 가지 특징으로, 예측 가능한 코드가 있다.

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을 통해 함수가 예측 가능한 코드가 되도록 하는 것이 중요하다.

2. 불변 객체는 근본적으로 thread-safe하여, 따로 동기화 할 필요 없다.

멀티 쓰레드 환경에서, 여러 클라이언트에서 한 가지의 객체의 값을 동시에 수정할 때 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);
}

3. setter-safe하다.

final 키워드가 붙은 인스턴스는 생성자를 통해 생성될 때 값을 초기화하거나, 고정된 값을 초기화 해주어야만 선언이 가능하다.
따라서 외부에서 동적으로 값을 변경할 가능성이 줄어든다.

4. 성능의 사소한 이점

런타임 시 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"가 한번에 할당되는 것을 확인할 수 있다.

단점

1. 값이 다르면 반드시 독립된 객체로 만들어야 한다.

새로운 독립된 객체를 만들 일이 많아진다면, 이를 만들고 기존에 필요없어진 객체를 제거하는 데에 모두 비용이 들어간다.
따라서, 합당한 이유(꼭 변경해야 함)가 있다면 꼭 final 키워드를 붙이지 않아도 좋다.

결론

단점에 비해 얻는 장점이 성능, 보안 등으로 확실하다.
따라서 위에서 언급한 것처럼 필연적으로 자주 변경되는 필드의 경우는 final을 붙이지 않는 것을 고려해볼 수 있지만, 가능한 final 키워드를 붙이는 것이 좋아보인다.

profile
newBlog == https://kdkdhoho.github.io

0개의 댓글