[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 기타내용





© 2021.02. by kirim

Powered by kkrim