ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JAVA] final 키워드 ( variable, method, list, class )
    IT/JAVA | Spring 2022. 7. 1. 19:28

    java의 fianl에 대해 다양하게 알아보자

     


    fianl의 구글 번역

     - 결정적인, 마지막의, 최후의, 목적을 나타내는, 말끝의

     

    java에서 final ( 출처 : 위키피디아 )

    -원문

    In the Java programming language, the final keyword is used in several contexts to define an entity that can only be assigned once.
    Once a final variable has been assigned, it always contains the same value. If a final variable holds a reference to an object, then the state of the object may be changed by operations on the object, but the variable will always refer to the same object (this property of final is called non-transitivity[1]). This applies also to arrays, because arrays are objects; if a final variable holds a reference to an array, then the components of the array may be changed by operations on the array, but the variable will always refer to the same array.[2]

     

    - 구글 번역

    자바 프로그래밍 언어에서 최종 키워드는 여러 컨텍스트에서 한 번만 할당할 수 있는 항목을 정의하는 데 사용됩니다. 
    최종 변수가 할당되면 항상 동일한 값을 포함합니다. 최종 변수가 객체에 대한 참조를 보유하는 경우 객체의 상태는 객체에 대한 작업에 의해 변경될 수 있지만 변수는 항상 동일한 객체를 참조합니다(최종 변수의 이 속성을 비전이성[1]이라고 함). . 배열은 객체이기 때문에 이것은 배열에도 적용됩니다. 최종 변수가 배열에 대한 참조를 보유하는 경우 배열의 구성요소는 배열에 대한 작업으로 변경될 수 있지만 변수는 항상 동일한 배열을 참조합니다.[2]
     

    final (Java) - Wikipedia

    From Wikipedia, the free encyclopedia Jump to navigation Jump to search In the Java programming language, the final keyword is used in several contexts to define an entity that can only be assigned once. Once a final variable has been assigned, it always c

    en.wikipedia.org

     

    요약하자면 java의 final은 단 한 번만 할당 가능하며 변경이 불가능하게 만드는 키워드이다.

     

    그렇다면 java에서 이 final 키워드를 어떻게 사용하는지 상황별로 확인해보자

     


    ※ variable ( 변수 )

    변수에 final 키워드를 사용하면 수정이 불가능한 변수를 만들 수 있다.

    final int finalNumberInit = 1; // 최초 1 할당 이후 변경할 수 없다.
    final int finalNumber;
    num = 1; // 최초 초기값이 할당되지 않아 데이터 할당 가능
    final int number = 1;
    number = 1 // 오류! 요즘은 IDE에서 잡아준다.

    이렇듯 fianl 키워드가 붙은 변수는 초기화 후 수정이 불가하다.

     

    모두 같은 값을 가져야 할 때 사용된다.

     

    보통 fianl field를 만들어 사용한다. 

     

    final 키워드와 static 키워드를 조합하여 상수를 만들고 전역에서 같은 값을 쉽게 호출하여 사용할 수 있다.

    class StaticFinalField {
    	static final staticFinalNum = 10;
    }
    
    class UserFinalField{
    	public static void main(String[] args) {
            if ( StaticFinalField.staticFinalNum < 20 ) {
                System.out.println("상수가 20보다 작음")
            }
        }
    }

    static 키워드는 모든 객체가 공유하는 static memory영역에 할당된다.

     

    다수의 객체가 같은 값을 가진 데이터를 고유해야 하며 그 데이터는 수정이 불가능한 상수여야 할 때 사용한다.

     


     method ( 메서드 )

     

    변수에 final을 붙이면 수정 불가능한 변수가 만들어진다. 

     

    그렇다면 method에 final이 붙으면 어떻게 될까?

     

    method는 변수와 달리 method 내부 로직을 수정하지 못하지 않나? 

     

    수정이 가능하다. java에는 오버라이딩을 통해 같은 이름의 메서드를 재정의한다

     

    final method는 method를 override하지 못하게 한다.

     

    변수와 큰 의미는 동일하다. final 키워드를 사용하는 곳은 초기화 후 수정이 불가능 하다.

    class FinalMethodInit {
        public final void finalMethod(){
            System.out.println("final method");
        }
    }
    
    public class OverridMethod extends FinalMethodInit{
        public void finalMethod() { // 컴파일 오류 발생
            System.out.println("final method override");
        }
    }

     

    거의 대부분의 개발툴에선 오류를 뱉어내며 컴파일조차 안될것이다. Intellj IDEA에서는 아래와 같은 오류를 뱉어낸다.

    'finalMethod()' cannot override 'finalMethod()' in 'FinalMethodInit'; overridden method is final

     

    해당 메소드는 절대 오버라이딩되면 안될때 사용한다. 즉 해당 메소드를 사용한다는건 모두 같은 로직에서 실행된 결과를 받게 된다.

     


     class (  클래스 , 객체 , List )

     

    아마도 해당 블로그에서 가장 긴 내용이 될것 같다.

     

    클래스와 객체, List를 묶어서 이야기한다.

     

    final이 붙은 변수와 메소드는 변경이 불가능하게 되었다. 변수를 새로운 값을 할당받지 못하고 메소드는 오버라이딩하지 못한다.

     

    결론은 final type class를 다른 class에서 상속받지 못한다. 즉 어떤 class의 부모가 되지 못한다.

     

    대표적인 예는 java에서 기본적으로 제공하는 java.lang.String이다.

     

    우리는 익히 String은 primitive type이 아니라 class라고 공부했다.

     

    억지로 java.lang.String 클래스를 상속시키면 컴파일에러가 발생하며 Intellij IDEA기준 아래와 같은 문구가 출력된다.

    final class인 String class를 상속받으려 하면 출력되는 오류 메세지 ( 번역 )

    안내 문구와 같이 primitive type의 wrapper class( Integer, Long ....) 들 역시 상속받을 수 없다.

     

    이제 객체와 List에 대해 알아본다.

     

    객체생성은 변수선언과 유사하다.

     

    final로 선언하면 동작하것도 비슷하다. 한번 생성된 객체는 재생성하지 못한다.

    class StaticFinalField {
        static final int num = 1;
        public final void finalMethod(){
            System.out.println("final method");
        }   
    }
    
    class FinalObjectInit {
        public static void main(String[] args) {
    
            final StaticFinalField staticFinalField = new StaticFinalField();
            staticFinalField = new StaticFinalField(); // 오류가 발생한다. 
        }
    }
    Cannot assign a value to final variable 'staticFinalField'

     

    여기까지는 쉽게 예상이 가능하다. 

     

    하지만 질문을 하나 던져본다.

     

    final로 생성된 VO는 setter로 데이터를 삽입 가능할까?

    final로 생성된 list는 list.add or list.remove와 같은 행위를 했을때 어떻게 동작할까?

     

    - VO

    class FinalObjectInit {
        public static void main(String[] args) {
            final VoTest voTest = new VoTest();
            voTest.setNum(1); // final이지만 setter가 동작할까?
            voTest.setText("테스트");
    
            System.out.println("voTest.toString() = " + voTest.toString()); // 어떤 데이터가 출력될까?
        }
    }
    
    class VoTest{
        private int num;
        private String text;
    
        public int getNum() {
            return num;
        }
    
        public void setNum(int num) {
            this.num = num;
        }
    
        public String getText() {
            return text;
        }
    
        public void setText(String text) {
            this.text = text;
        }
    
        @Override
        public String toString() {
            return "VoTest{" +
                    "num=" + num +
                    ", text='" + text + '\'' +
                    '}';
        }
    }
    voTest.toString() = VoTest{num=1, text='테스트'}

     

    결과는 데이터가 변경 되었다.

     

    - List

    class FinalObjectInit {
        public static void main(String[] args) {
            final List<String> listTest = new ArrayList<>();
            listTest.add("add 1"); // add가 될까?
            listTest.add("add 2");
            listTest.add("add 3");
            listTest.add("add 4");
    
    		// 데이터는 어떻게 출력될까?
            System.out.println("listTest.toString() = " + listTest.toString());
        }
    }
    listTest.toString() = [add 1, add 2, add 3, add 4]

     

    정상적으로 add가 동작했다. 

     

    둘 모두 fianl 키워드를 사용하여 불변객체를 만들었지만 실제 내부의 데이터가 변경되었다.

     

    fianl 키워드를 사용해서 객체를 생성하면 객체를 재생성하지 못하지만 내부의 데이터는 변경 가능하다.

     

    우리가 객체를 생성하면 해당 변수는 데이터를 바라보는것이 아니라 메모리의 주소를 바라본다고 배웠다.

     

    final 키워드로 객체를 생성했다면 해당 객체가 생성되어 메모리에 올라가있는 메모리 주소를 변경하지 못하는 것이고 

     

    해당 객체 내부의 변경행위를 막지 않는다.

    final List<String> listTest = new ArrayList<>(Arrays.asList("test1","test2","test3"));
    
    System.out.println("listTest.toString() = " + listTest.toString());
    //listTest.toString() = [test1, test2, test3]
    
    listTest.add("add 1");
    listTest.add("add 2");
    listTest.add("add 3");
    listTest.add("add 4");
    
    System.out.println("listTest.toString() = " + listTest.toString());
    // listTest.toString() = [test1, test2, test3, add 1, add 2, add 3, add 4]

    그리하여 위와 같은 결과가 나타나게 된다. 

    댓글

Designed by Tistory.