Java Academy · 课时

对密封类型使用穷尽式 switch

编译时完整性检查

第 4 / 4 课13 个步骤

对密封类型使用穷尽式 switch 是 CoddyKit 上的免费 Java Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。

穷举式分支选择

当您针对密封类型使用 switch 时,编译器知道每个允许的子类型。如果分支选择覆盖了所有子类型,它就是穷举式的,您不需要默认分支。

类型模式分支选择

分支选择表达式可以使用类型模式,根据密封值的运行时类型进行匹配。

public class Main {
    sealed interface Shape permits Circle, Square {}
    record Circle(double radius) implements Shape {}
    record Square(double side) implements Shape {}

    static String describe(Shape s) {
        return switch (s) {
            case Circle c -> "circle r=" + c.radius();
            case Square sq -> "square s=" + sq.side();
        };
    }

    public static void main(String[] args) {
        System.out.println(describe(new Circle(2)));
        System.out.println(describe(new Square(3)));
    }
}

无需默认分支

由于 Circle 和 Square 是唯一允许的子类型,编译器会接受这个完整的分支选择。添加 default 会显得多余。

编译时完整性

如果之后添加了新的允许子类型,却忘记处理它,那么每个穷举式分支选择都会无法编译。这样可以将一整类运行时错误转变为编译错误。

计算 Result

穷举式分支选择表达式非常适合将一个值归约为 area 等结果。

public class Main {
    sealed interface Shape permits Circle, Square {}
    record Circle(double radius) implements Shape {}
    record Square(double side) implements Shape {}

    static double area(Shape s) {
        return switch (s) {
            case Circle c -> Math.PI * c.radius() * c.radius();
            case Square sq -> sq.side() * sq.side();
        };
    }

    public static void main(String[] args) {
        System.out.printf("%.2f%n", area(new Circle(1)));
        System.out.printf("%.2f%n", area(new Square(2)));
    }
}

分支选择语句与表达式的区别

完整覆盖适用于分支选择表达式(能够产生值的分支选择)以及在密封类型上使用箭头形式的分支选择语句。编译器会强制要求覆盖所有情况。

处理空值

默认情况下,如果引用类型上的分支选择接收到空值,就会抛出 NullPointerException。您可以添加 case null 标签来明确处理空值。

public class Main {
    sealed interface Shape permits Circle, Square {}
    record Circle(double radius) implements Shape {}
    record Square(double side) implements Shape {}

    static String describe(Shape s) {
        return switch (s) {
            case null -> "no shape";
            case Circle c -> "circle";
            case Square sq -> "square";
        };
    }

    public static void main(String[] args) {
        System.out.println(describe(null));
        System.out.println(describe(new Circle(1)));
    }
}

守卫不会破坏穷举完整性

您可以添加 when 守卫来细化分支,但单独使用带守卫的分支并不能覆盖某个类型。要保持穷举完整性,您仍然需要为每个子类型提供不带守卫的分支。

public class Main {
    sealed interface Shape permits Circle, Square {}
    record Circle(double radius) implements Shape {}
    record Square(double side) implements Shape {}

    static String size(Shape s) {
        return switch (s) {
            case Circle c when c.radius() > 10 -> "big circle";
            case Circle c -> "small circle";
            case Square sq -> "square";
        };
    }

    public static void main(String[] args) {
        System.out.println(size(new Circle(20)));
        System.out.println(size(new Circle(2)));
    }
}

对密封类执行分支选择

同样的穷举完整性也适用于密封类,而不仅仅是接口。

public class Main {
    sealed static abstract class Token permits Word, Number {}
    static final class Word extends Token { final String text; Word(String t){text=t;} }
    static final class Number extends Token { final int value; Number(int v){value=v;} }

    static String show(Token t) {
        return switch (t) {
            case Word w -> "word:" + w.text;
            case Number n -> "num:" + n.value;
        };
    }

    public static void main(String[] args) {
        System.out.println(show(new Word("hi")));
        System.out.println(show(new Number(7)));
    }
}

为什么这很强大

穷举式分支选择与密封类型结合后,可以让代码既安全又易于演进。所有情况都记录在同一个位置,当模型扩展时,编译器会保证您不会遗漏任何情况。

完整示例

这里将一个密封结果类型归约为一条消息,并完整覆盖所有情况。

public class Main {
    sealed interface Result permits Ok, Err {}
    record Ok(int value) implements Result {}
    record Err(String message) implements Result {}

    static String render(Result r) {
        return switch (r) {
            case Ok ok -> "value = " + ok.value();
            case Err err -> "error: " + err.message();
        };
    }

    public static void main(String[] args) {
        System.out.println(render(new Ok(10)));
        System.out.println(render(new Err("bad input")));
    }
}

快速检查

请检验您对穷举式分支选择的理解。

回顾

您学会了在密封类型上使用穷举式分支选择。

  • 覆盖所有允许的子类型会使分支选择具有穷举性,因此不需要 default。
  • 遗漏新的子类型会变成编译错误。
  • case null 可以明确处理空值。
  • 守卫可以细化分支,但不能替代不带守卫的分支。
免费开始

用 AI 导师学习 Java — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
104
课程
374

常见问题解答

「对密封类型使用穷尽式 switch」课时是免费的吗?

是的 — 「对密封类型使用穷尽式 switch」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。

「对密封类型使用穷尽式 switch」这节课中我会学到什么?

编译时完整性检查 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Java Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「对密封类型使用穷尽式 switch」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Java Academy 课中编写并运行代码吗?

能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 声明密封类型
  2. permits 子句
  3. 使用 Record 实现密封类型
  4. 对密封类型使用穷尽式 switch
← 返回 Java Academy