추상 클래스 문서의 구현 코드다.

// 추상 클래스: 공통 필드·생성자·메서드는 부모에, 자식마다 다른 몸통은 추상 메서드로
// 실행: docker run --rm -v "$PWD":/w -w /w eclipse-temurin:21-jdk java 24_java-abstract-class_impl.java
// 문서(24.추상 클래스.md)에 실은 출력과 카드의 답을 직접 비교한다.
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Modifier;

class AbstractClassCheck {
    static String capture(Runnable r) {
        PrintStream old = System.out;
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        System.setOut(new 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.println("ok: " + name);
    }

    public static void main(String[] args) {
        check("예시: Dog와 Cat", capture(() -> {
            Animal a = new Dog("바둑이");
            a.breathe();
            a.sound();
            a = new Cat("나비");
            a.breathe();
            a.sound();
        }), "바둑이: 숨을 쉰다\n멍멍\n나비: 숨을 쉰다\n야옹\n");

        // 추상 클래스에도 생성자가 있고, 자식 객체를 만들 때 먼저 실행된다
        check("카드 C3: 생성자 순서", capture(() -> {
            Shape s = new Circle();
            s.describe();
        }), "Shape 생성자\nCircle 생성자\n넓이를 잴 수 있는 도형: 원\n");

        if (!Modifier.isAbstract(Animal.class.getModifiers())) throw new AssertionError("Animal은 abstract여야 한다");
        System.out.println("ok: Animal은 abstract 클래스");

        System.out.println("ALL CHECKS PASSED");
    }
}

abstract class Animal {
    String name;                       // 공통 필드

    Animal(String name) {              // 생성자도 둘 수 있다
        this.name = name;
    }

    void breathe() {                   // 몸통이 있는 보통 메서드
        System.out.println(name + ": 숨을 쉰다");
    }

    abstract void sound();             // 추상 메서드: 선언만 있고 몸통이 없다
}

class Dog extends Animal {
    Dog(String name) { super(name); }
    @Override
    void sound() { System.out.println("멍멍"); }
}

class Cat extends Animal {
    Cat(String name) { super(name); }
    @Override
    void sound() { System.out.println("야옹"); }
}

abstract class Shape {
    Shape() { System.out.println("Shape 생성자"); }
    abstract String name();
    void describe() { System.out.println("넓이를 잴 수 있는 도형: " + name()); }
}

class Circle extends Shape {
    Circle() { System.out.println("Circle 생성자"); }
    @Override
    String name() { return "원"; }
}