📜 제목으로 보기

final 키워드로 값의 변경을 막아라

final 세팅 in IDE, 지역 변수, 메소드 파라미터

  • 지역변수, 파라미터 모두 final로 생성되도록 하자
    • 설정 > Editor > Code style > java > Code generation > Make ~ final 체크박스 2개 final_세팅
  • main 메소드 template 인 psvm의 인자에도 final을 붙혀준다.

    • 설정 > live template or psvm 검색 > final 붙여주기

    image-20220220230643712

final 값 -> 재할당을 막아 불변

  • 메서드 내부에서 코드 중간에서 가변 변수를 수정할 때, 위에 재할당 등의 수정이 되었었나 확인해야한다.
    • final로 재할당(재사용)이 불가능한 불변 값사용하다가, 변수의 재사용이 필요할 경우 새 변수를 만들어서 쓰자.
  • 모든 지역변수, 인자에 final을 붙이자.

    image-20220221090040904

  • final로 값을 초기화(선언후 할당)하는 방법은 2가지다

      //1. final변수 선언과 동시에 && 할당 -> final은 재할당 불가 -> 변수재사용시 새객체 반환할 것
      final int i = 2;
      i = 4; // java: cannot assign a value to final variable i
        
        
      //2. final변수 먼저 선언 -> 이후 할당
      // 생성자 등에서 값을 외부에서 받아와 추가 로직(convert or default 대입 등 특수 case(if) 존재)이후 초기화
      final int number;
      number = 5;
    

final 객체 -> 메서드로 내부 변화 가능 -> 메서드를 항상 새 객체로 응답

  • 객체의 경우는 final로는 완벽한 불변을 못만든다.

    • final 값와 달리 final 객체는 메소드 등에 의해 내부가 변할 수 있다.(포인터의 포인터에서 껍데기 포인터1)
  • 객체를 불변으로 만든 법상태변화를 일으키는 기능들은 새로운 객체를 반환하도록 정의하자.

    1. 상태변화 시켰던 메서드의 void returnType에 해당 객체를 응답(return)하도록 수정

       // 내부변화되는 final 객체
       public void move() {
           this.position++;
       }
              
              
       // 1. 일단 응답을 동일 객체로 만들어주기
       public Car move() {
           this.position++;
       }
      
    2. 변화된 상태로 new생성자()로 호출하여 -> 새로운 객체를 반환시킨다.

       //2. 응답 객체를, 변화된 값으로 새로 만들어서 응답 -> 내부 값은 변화 없다.
       public Car move() {
           return new Car(this.position + 1);
       }
              
       //3. 생성자를 통해 응답할 객체를 만들어준다. 내부 값의 변화는 없게 한다.
       public Car(final int position) {
           this.position = position;
       }
      

final 메서드, 클래스 -> 자식이 못 쓰게

package domain;

final public class FinalParent {
    final void finalMethod() {
        final String asdfasdf = "asdfasdf";
    }
}
package domain;
//2. final 클래스는 자식이 상속이 안된다.
// java: cannot inherit from final domain.FinalParent
public class FinalChild extends FinalParent {
    
    //1. final 메소드는 자식이 오버라이딩 못된다. -> 코어 부분에서 변경을 원치 않는 메소드를 명시할 때 사용
    //java: cannot find symbol,  symbol: class FinalParent
    @Override
    protected void finalize() throws Throwable {
        super.finalize();

    }
}

final 컬렉션(내부 변경api를 제공하는 껍데기포인터1) 등은 응답시 copy(unmodifiable) 해서 응답하라

  • my) 포인터1의 포인터2에 의해 변경될 수 있으니, 껍데기 포인터1 자체를 copy해서 응답해라
  • 컬렉션을 반환할 때, 메모리주소가 공유되니, 어디서든 같은 컬렉션이 변경되는 위험이 있다.
    • copy해서 전혀 다른 메모리의 컬렉션으로 응답하자.
List<String> names = List.of(); // unmodifiableList
names.add("텍스트") // 예외발생
- 내가 만든 일급컬렉션이라면 불변(내부 변화 기능 제공 않함)이 되겠지만, **일반 컬렉션이라면, 반환시 메모리주소가 공유된다. -> `unmodifiableList`로 copy된 메모리를 반환시켜 테스트 등 변화가 발생하게 하자.**

컨밴션 중 하나가 된다.

  • final을 붙이는 것은 컨벤션이며 팀 안에서는 따르는게 좋을 수 도 있다.
    • 내 생각에 타당하지 않으면 얘기를 꺼내서 바꿀 수 있다.
  • 컨벤션이라면, 비효율적이라도 그대로 가는 경우가 많다.
    • 디자인 패턴 역시, 효율적이거나 좋은 코드라서 뿐만 아니라 커뮤니케이션을 줄이는 컨밴션이기 때문에 좋기도 하다.

참고 글