본문으로 건너뛰기
홈
기술
기술 전체
프로그래밍68
컴퓨터 과학63
AI48
웹 개발36
인프라33
데이터31
소프트웨어 공학18
소개
← 목록으로프로그래밍 › Java › 기초

java final 키워드

목차

Java final 키워드

final은 클래스, 메서드, 변수에 붙일 수 있다. 붙이는 위치에 따라 막는 동작이 다르다. 클래스에서는 상속, 메서드에서는 오버라이드, 변수에서는 재할당을 막는다.

1. final 클래스: 상속을 막기

public final class A {
}

// 컴파일 오류: final 클래스 A를 상속할 수 없다.
// public class B extends A { }

더 이상 하위 타입으로 확장하지 않을 클래스를 명시할 때 사용한다. 다만 final이 붙었다고 객체의 모든 상태가 불변이 되는 것은 아니다. 클래스 안에 변경 가능한 필드가 있다면 상태는 바뀔 수 있다.

2. final 메서드: 오버라이드를 막기

public class A {
    public final void printName() {
        System.out.println("A");
    }
}

class B extends A {
    // 컴파일 오류: final 메서드를 오버라이드할 수 없다.
    // @Override public void printName() { }
}

B는 A를 상속할 수 있고 printName()을 호출할 수도 있다. 막히는 것은 메서드의 재정의다. 상속을 완전히 막고 싶다면 메서드가 아니라 클래스에 final을 붙여야 한다.

3. final 변수: 한 번만 할당하기

public class B {
    private final int id;

    public B(int id) {
        this.id = id; // 생성자에서 최초 할당
    }

    public void change() {
        // this.id = 23; // 컴파일 오류: 재할당 불가
    }
}

초기값을 선언할 때 주지 않은 final 인스턴스 필드는 생성자가 끝나기 전에 할당해야 한다. 지역 변수에도 final을 붙일 수 있다.

final int count = 2;
// count = 3; // 컴파일 오류

참조 변수의 final은 참조를 바꾸지 못하게 할 뿐, 참조 대상 객체를 자동으로 불변으로 만들지는 않는다.

final StringBuilder name = new StringBuilder("A");
name.append("B"); // 객체 내용 변경은 가능
// name = new StringBuilder("C"); // 참조 재할당은 불가

따라서 변경할 수 없는 값을 원한다면 final과 함께 객체 자체의 설계도 살펴봐야 한다. 자세한 규칙은 Java 언어 명세의 클래스와 변수 절을 참고한다.

메서드 계약과 불변성을 구분하기

final 메서드는 하위 클래스가 동작을 바꾸지 못하게 한다. 호출 결과가 늘 같은 값을 준다는 뜻은 아니다. 메서드가 객체의 가변 필드를 읽으면 호출 시점에 따라 다른 결과를 낼 수 있다. final 클래스도 필드가 가변이면 인스턴스가 변한다. 따라서 외부에 불변 객체를 제공하려면 필드를 적절히 감추고 생성자에서 상태를 확정하며, 가변 객체를 받은 경우 방어적으로 복사해야 한다.

final class Team {
    private final java.util.List<String> members;
    Team(java.util.List<String> members) {
        this.members = java.util.List.copyOf(members);
    }
    java.util.List<String> members() { return members; }
}

여기서는 final 클래스·필드와 List.copyOf를 함께 사용한다. 생성자에 넘긴 원본 목록이 나중에 바뀌어도 저장한 목록은 바뀌지 않고, 반환 목록을 호출자가 수정할 수도 없다. 그러나 원소가 변경 가능한 객체라면 그 객체의 내부 상태까지 깊게 복사되는 것은 아니다. final을 붙이는 위치와 실제로 지켜야 할 불변 조건을 분리해 생각해야 한다.

람다식이 바깥 지역 변수를 사용할 때도 그 변수는 final이거나 사실상 final이어야 한다. 이는 이름의 재할당 규칙이고, 여러 스레드가 공유하는 가변 객체의 동시성 안전성을 자동으로 보장하지 않는다.

같은 카테고리의 글