final과 상수 문서의 구현 코드다.

// 실행: docker run --rm -v "$PWD":/w -w /w eclipse-temurin:21-jdk java 17_java-final_impl.java
// (JDK 21. 파일 이름이 숫자로 시작해 public 클래스를 두지 않는다. 맨 위 클래스의 main이 돈다.)
// 문서 「final과 상수」의 예제 출력과 카드 답을 실제 실행 결과와 비교한다.

import java.util.Arrays;

class FinalCheck {
    // final 필드: 선언할 때 넣거나(school) 생성자에서 넣는다(id). 그 뒤로는 못 바꾼다
    static void finalField() {
        Student a = new Student("2024001", "Alice");
        Student b = new Student("2024002", "Bob");
        a.name = "Alicia";      // final이 아닌 필드는 바꿀 수 있다
        System.out.println(a.school + " " + a.id + " " + a.name);
        System.out.println(b.school + " " + b.id + " " + b.name);
    }

    // 상수: static final. 객체 없이 클래스 이름으로 쓴다
    static void constant() {
        System.out.println(Game.MAX_HEALTH);
        System.out.println(10 * 10 * Game.PI);
    }

    // final은 "변수가 가리키는 대상"을 못 바꾸게 할 뿐, 대상의 속은 막지 않는다
    static void finalReference() {
        Squad s = new Squad();
        s.healths[0] = 100;
        System.out.println(Arrays.toString(s.healths));
    }

    public static void main(String[] args) {
        check("final field", capture(FinalCheck::finalField),
              "CAU 2024001 Alicia\nCAU 2024002 Bob\n");
        check("constant", capture(FinalCheck::constant), "45\n314.159\n");
        check("final reference", capture(FinalCheck::finalReference), "[100, 45, 45]\n");
        System.out.println("ALL CHECKS PASSED");
    }

    static String capture(Runnable r) {
        java.io.PrintStream old = System.out;
        java.io.ByteArrayOutputStream buf = new java.io.ByteArrayOutputStream();
        System.setOut(new java.io.PrintStream(buf));
        try { r.run(); } finally { System.setOut(old); }
        return buf.toString();
    }

    static void check(String name, String got, String want) {
        if (!got.equals(want)) throw new AssertionError(name + "\n--- got ---\n" + got + "--- want ---\n" + want);
        System.out.print("[" + name + "]\n" + got);
    }
}

class Student {
    final String school = "CAU";   // 선언할 때 값을 준다
    final String id;               // 생성자에서 값을 준다
    String name;

    Student(String id, String name) {
        this.id = id;
        this.name = name;
    }
}

class Game {
    static final int MAX_HEALTH = 45;
    static final double PI = 3.14159;
}

class Squad {
    final int[] healths = {45, 45, 45};
}