변수에서의 final 키워드

** 이 포스트 에서는 final 키워드가 변수 혹은 객체 앞에 붙었을 때 어떻게 동작 하는지 알아보겠습니다 **

# Final keyword

원시타입에 선언하는 final 키워드와 객체에 선언하는 final 키워드는 동일한 역할을 합니다.
변수를 만들면서 할당한 초기값을 누군가가 다른 값으로 변경하지 못하도록 하는 역할

1
2
3
4
5
6
7
8
@Test
public void testFinalKeyword() {
final int a = 990;
a++; //컴파일 에러

final List<String> list = new ArrayList<>();
list = new ArrayList<>(); //컴파일 에러
}

위의 코드에서와 같이 원시타입인 int형 변수를 정의할때 990이라는 초기값을 주고 그 아랫줄에서 a++ 를 시도하면 컴파일 에러가 발생 합니다.

이클립스에서는 "The final local variable a cannot be assigned. It must be blank and not using a compound assignment" 라고 X표가 쳐져 있네요.

참조타입인 List형 변수도 마찬가지로 list를 정의할때 new ArrayList()로 새로운 객체에 한번 할당 하고 다른 객체에 다시 할당할 수 없습니다. 하지만 객체자체를 바꾸는 것은 불가능 하지만 한번 생성된 객체의 내부값들은 변경할 수 있다는 점이 원시타입의 final과는 구분되는 점 입니다.

1
2
3
4
5
6
7
8
9
@Test
public void testFinalKeyword() {
final List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
assertEquals(2, list.size());
list.clear();
assertEquals(0, list.size());
}

위의 코드를 보면 final로 생성한 list에 add()하고 clear() 하는등의 조작을 가해도 문제없이 동작 하는것을 알 수 있습니다.

일반적으로 final 변수는 프로그램 전체에 걸쳐 사용되는 경우가 많아서 앞에서 예로 들었던 경우처럼 사용되는 경우는 거의 없고 클래스에 static과 함께 선언하여 사용합니다. 원시타입 (Primitive Type) 포스트에 예로 많이 들었던 Integer.MAX_VALUE나 Integer.MIN_VALUE 등과 같이 클래스이름으로 직접 접근하는 객체들이 이에 해당하는 경우라고 할 수 있습니다.

** list로 예를 든 것 같이 immutable class가 아닌 객체 앞에 final을 붙이면 객체 자체를 다시 new() 로 새로 할당 할 순 없지만 내부값들은 변경 가능하다는 점은 잘 기억해서 실수 하는 일이 없어야 겠습니다. **

# Code Convention

static final 변수 에 대해서는 곁다리로 추가 설명해 드리고 넘어가야 할 점이 있는데요 꼭 지키지는 않아도 되지만 되도록 지키는게 더 좋은 코드 컨벤션에 대한 이야기 입니다.

  • 클래스 안에 static final로 변수를 선언할 때에는 변수명을 모두 대문자로 합니다.
  • static final 변수에서 두단어가 합쳐질때는 언더스코어(단어_단어)로 명명 합니다.
  • 로컬변수는 첫글자가 소문자로 시작하는 camelCase 로 정의 합니다.

static final 변수도 그대로 로컬변수처럼 선언하는 경우가 많이 있습니다.
그렇게 선언 하는 것이 프로그램적으로 잘못된 것이 아니고 혼자 개발 하는것 이라면 상관 없겠지만

그래도 더불어 사는세상 법으로 제정되진 않았지만 지켜야할 덕목이 있는것처럼 코드 컨벤션은 지키려고 노력하는게 좋겠습니다.

특히 요즘은 깃헙 에 오픈소스로 공개하는 경우가 많은데 코드 컨벤션을 지켜서 올리면 같이 프로젝트에 참여하는 사람들이 보다 쉽게 코드를 이해하는데 도움이 될 것입니다.

1
2
3
4
5
6
7
8
9
10
11
12
public class PrimitiveType {

public static final int LIMIT=10;
public static final String MY_NAME="haha";

public int absIntegerValue(int intValue) {
if (intValue == Integer.MIN_VALUE) {
throw new NumberFormatException();
}
return Math.abs(intValue);
}
}

위와같이 PrimitiveType 클래스내부에 static final로 변수를 설정하고 나면
혹시라도 다른 클래스에서 저 클래스의 객체를 new PrimitiveType() 등으로 생성하게 되었을 경우 그 객체의 참조변수에서도 바로 접근이 가능 합니다. 하지만 코딩 규약에서는 그렇게 하는것을 권장하지 않습니다.

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void testJavaCodingConvention(){

int myLimit = 5;

//좋지않은 방법
PrimitiveType pt = new PrimitiveType();
assertTrue(myLimit < pt.LIMIT);

//권장하는 방법
assertTrue(myLimit < PrimitiveType.LIMIT);
}

코드 컨벤션에 관심 있으신 분들은 내용이 방대하긴 하지만 아래 링크들을 참조해 보시기 바랍니다.

Google Java Style Guide
Code Conventions for the Java Programming Language

Share