[Java] final 키워드
1️⃣ final 키워드란?
- C언어에서 const 키워드와 비슷한 개념입니다.
const키워드처럼 변수를 상수화 시켜주는 키워드로 수정할 수 없게 해줍니다. 하지만 위키피디아에서 final키워드의 뜻을 보면 다음과 같습니다. > final 키워드는 한 번만 할당 할 수 엔티티를 정의하기 위해 여러 문맥에서 사용된다.
즉, 자바에서 **final**키워드는 선언만하고 나중에 초기화시켜줄 수 있습니다. 컴파일 중에 결정되는 **const**와 달리 **final**은 **런타임**중에 결정됩니다.
2️⃣ final 변수 초기화
- 위에서 말했듯이 final변수를 초기화를 나중에 할 수 있습니다.
- 메서드에서 생성된 final변수의 경우 다음과 같이 메소드내에서 초기화가 가능합니다.
public void sampleFunc(int num) {
final int SAMPLE;
SAMPLE = num;
}
- 하지만 클래스의
멤버변수 일 경우생성자 에서만 초기화가 가능합니다.
public class SampleClass {
static final int SAMPLE;
public SampleClass (int sample) {
SAMPLE = sample;
}
}
3️⃣ final키워드를 매개변수에 쓴다면?
(1) C언어와 비교
- C언어에서는 함수의 매개변수에 const키워드를 붙여서 원본이 실수로 변경되는 것을 막거나 명시해주는 역할을 해주었습니다.
C언어에서 const키워드의 위치에 따라
값 혹은주소 가 보호됩니다. 그리고 대부분 매개변수에서 const키워드는값 을 보호하기 위해 사용하는 것이 의미있다고 생각합니다.
(개인적으로 아직 이와같이void func(char* const s1)
매개변수를 사용한 것을 본 경험이 없습니다. 있을 수도..)하지만 Java에서는 포인터라는 개념이 없습니다. Array(배열), String, 클래스 변수와 같은 참조형 변수들을 매개변수로 받게 되면
값의 복사가 아닌 참조형 으로 받습니다. 이상한 것은 int와 같은 기본 자료형은값의 복사 로 매개변수를 받습니다. (C언어에서는int*
를 이용하여 참조형으로 받아올 수 있는 것과 상반됩니다.)- 하지만 잠시
int도 참조형으로 받는데 값복사처럼 보이는게 아닐까?
라고 생각했었습니다.
// 비교할 예시 코드(Java)
void func(int nb) {
nb = 5;
}
- 순수히 변수
nb
만을 변경해야 합니다.
// 참조형으로 가정할 경우(C언어)
void func(int* nb)
{
nb = 5; // 컴파일 에러
int sample = 5;
nb = &sample;
}
// 값복사로 가정할 경우(C언어)
void func(int nb)
{
nb = 5;
}
- 위의 예시에서 참조형이라 가정하면 값
5
가 독립된 주소를 가지도록 만드는 과정이 필요합니다. 그렇기 때문에int형
과 같은기본자료형 은 값복사가 맞는 것같습니다. - 간단히 생각하면 클래스자료형은
*
를 가지고 있고(변수가 주소) 기본자료형은*
을 가지고 있지 않다(변수가 값). - 결론적으로 말하고 싶은 것은
"매개변수(기본자료형제외)에서 사용하는 final은 주소값만 보호한다" 입니다.
(2) 클래스의 멤버변수는 변경이 된다
- 그렇기 때문에 클래스자체를 바꾸지 않는 이상
멤버변수의 변경 이 허용됩니다. 즉, 원본의 멤버변수를 변경할 수 있습니다.
public static void changeValue(final Test sample) {
sample.number = 4;
sample.string = "Goodbye!";
}
(3) 클래스 자체를 변경하는 것은 막아준다.
- 당연히 클래스 자체를 변경하는 것은 막아줍니다.
public static Test changeAddress(final Test sample) {
Test sample2 = new Test(6, "Hi!");
sample = sample2; // 컴파일 오류
return sample;
}
(4) final을 배열에 사용
- 결과적으로 클래스에서 사용할 때와 같습니다.
< final을 사용한 배열 >
public class Main {
public static void main(String[] args) {
int sample[] = {1, 2, 3};
/* 출력코드 생략 */
changeArray(sample);
/* 출력코드 생략 */
}
public static void changeArray(final int[] array) {
array[0] = 7;
}
}
123
723
int[]
(ArrayList 또한)꼴의 배열역시 final키워드를 사용해도 요소의값의 변경이 허용됩니다. 자바에는 포인터개념이 없기 때문에 요소값의 변경을 막는 방법은 없습니다.
(4) 고찰
- final키워드를 매개변수에 사용하는 경우에 대해 생각해봤습니다. 결론적으로 클래스의 원본은 막아줄 수 없었습니다.
- 원본이 변경되는 것을 막기위해 사용되는 경우가 많다고 생각하기 때문에
(생각보다 매개변수에도 많이 사용하는 듯합니다. 메소드내에서 원본이 변경이 되면 안됨을 알리는 용도(?))"매개변수에 final키워드를 사용하는 경우는 드물다" 라고 생각합니다. - 차라리 클래스내부에서 멤버변수에 사용하는 경우가 많을 것 같습니다.
- 추가적으로 클래스 내부에 ArrayList, 배열, 클래스변수가 (privat, final등등으로)캡슐화가 잘되어 있어도 getter로 반환한다면 변경될 위험이 있습니다. (외부에서 멤버변수에 직접적으로 접근하는 것은 OOP정신과 멀다고 생각합니다, 단, String과 같은
immutable클래스
는 제외)
3️⃣ final 기타내용
- final키워드는 메소드, 클래스에서 상속방지, 다형성(오버라이드)방지 등등.. 다양한 목적으로 사용될 수있습니다.
- 다음으로 final키워드에 관한 좋은글이 있어서 링크를 남기겠습니다.
>>>> 왜 자바에서 final 멤버 변수는 관례적으로 static을 붙일까? -Uno Kim