Notice
Recent Posts
Recent Comments
Link
«   2024/03   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Archives
Today
Total
관리 메뉴

pungjoo

Static final fields(primitive or String type) 사용 주의 본문

카테고리 없음

Static final fields(primitive or String type) 사용 주의

pungjoo.kim 2012. 4. 23. 11:25

’Foo.class’에 상수를 정의하고 'Bar.class’에서 ‘Foo.class’에서 정의한 상수를 사용하고 있다.
‘Foo.class’에 정의된 상수를 변경 및 컴파일 했을 때 'B.class’는 재컴파일하지 않고 ‘Foo.class’에서 변경한 상수에 대한 영향을 받아야 한다.

 

1. 들어 가며..

변하지 않는 값을 '상수(constant)'라고 합니다. 말 그대로 변할 수 없는 값입니다. 예를 들어 long type의 값인 100L이라는 상수를 선언하고자 한다면 통상 다음과 같이 선언 합니다.

final static public long = 100L;

그리고 통상적으로 상수들은 집합체인 상수 class에 모아둡니다. 이렇게 하는 이유는 크게 두가지 사유가 있습니다.

1. 여기 저기 흩어져 있을 경우 수정하려 하면 많은 class를 찾아서 수정해야 하므로 불편함.
2. 1번 같이 한 곳에 모아 두고 변경이 있을 경우 모아둔 class만 컴파일해도 참조하는 class를 재컴파일하지 않고 runtime시에 영향을 받기 위해.

그러나 위와 같이 ‘final static'으로 선언된 ‘primitive / String’ type은 생각처럼 작동하지 않습니다.

첨부된 ‘The Java Language Specification, Third Edition’에서 field에 대해서 부분적으로 많은 부분에서 설명하고 있습니다. 그 중에 다음과 같은 부분을 발췌했습니다. langspe-3.0.pdf / 348~349page ( Adobe reader 382~383 page)

The best way to avoid problems with 'inconstant constants' in widely-distributed code is to declare as compile time constants only values which truly are unlikely ever to change. Other than for true mathematical constants, we recommend that source code make very sparing use of class variables that are declared static and final. If the read-only nature of final is required, a better choice is to declare a private static variable and a suitable accessor method to get its value. Thus we recommend:
private static int N;
public static int getN() { return N; }
rather than:
public static final int N = ...;
There is no problem with:
public static int N = ...;
if N need not be read-only. We also recommend, as a general rule, that only truly constant values be declared in interfaces.We note, but do not recommend, that if a field of primitive type of an interface may change, its value may be expressed idiomatically as in:
interface Flags {
boolean debug = new Boolean(true).booleanValue();
}
insuring that this value is not a constant. Similar idioms exist for the other primitive types. One other thing to note is that static final fields that have constant values (whether of primitive or String type) must never appear to have the default initial value for their type (§4.12.5). This means that all such fields appear to be initialized first during class initialization (§8.3.2.1, §9.3.1, §12.4.2).


2. 현실

다음과 같이 Constants와 Bar가 있습니다. Bar는 Constants에 정의된 Foo를 참조합니다.



이때 Constants의 Foo를 변경하고 Constants만 컴파일 후에 실행하면 어떻게 될까요? 
다음은 ‘Foo =100L;’을 ‘Foo = 500L;’으로 변경한 결과 입니다.

예측으로는 value가 '525'가 나와야 합니다만 '125'가 나옵니다.


과연 어떻게 컴파일되었기에 그럴까요? 해서 디컴파일해서 내부를 보겠습니다.

우리가 원하는 결과는 'long foo = Constants.Foo + 25L;' 이라 생각 했지만, 'long l = 125L;'로  컴파일 되어 있네요.


3. 이유

‘primitive/String’ type에 final static 이 선언되면 컴파일시에 분석기가 상수로 판단해 위 처럼 참조로 컴파일하지 않고 정적으로 값을 넣어 주게 됩니다.

따라서 위와 같은 현상을 피하려면 final을 제거해 주면 되나, 애초에 상수라는 것은 runtime/컴파일시 어느 곳에서도 변경을 가할 수 없게 하기 위해서 final을 선언해 주기 때문에 근본적인 해결 책은 되지 못 합니다.

해서 가급적이면 primitive type을 사용하지 않고 object type을 사용해야 합니다.

final static public long Foo = 100L;

을 다음과 같이

final static public long Foo = new Long(100L).longValue();


4. 방안



위와 같이 변경하면 Constants만 변경하고 컴파일하면 원하는 결과를 얻게 됩니다.
주의 할 부분이 하나 있습니다. String type은 object type이 맞지만 다음과 같이 해야 합니다.

final static public String MSG = “메시지”;

를 다음과 같이

final static public String MSG = new String( “메시지" );

 

 

@

Comments