尝试在 switch 表达式中记录模式时出现 Java 19 编译器问题

Msc*_*scG 5 java record pattern-matching java-19

我从这里下载了 JDK19 的候选版本https://jdk.java.net/19/以尝试一下那里实现的新记录模式,但我遇到了一些问题。在我的测试中,我写了一个基于密封接口加记录的Optional版本,方式如下:

package tests.patterns;

import java.util.Objects;

public class TestRecordPatter
{
    public static void main(final String[] args)
    {
        final Opt<String> opt1 = computeAnswer(23);
        final String answer1 = switch (opt1) {
            case Opt.Some<String>(String ans) -> ans;
            case Opt.None __ -> "no answer";
            default -> throw new IllegalStateException("This should not happen"); // A
        };
        System.out.println(answer1);

        final Opt<String> opt2 = computeAnswer(35);
        final Object answer2 = switch (opt2) { // B
            case Opt.Some<String>(var ans) -> ans; // C
            case Opt.None __ -> "no answer";
            default -> throw new IllegalStateException("This should not happen"); // A-2
        };
        System.out.println(answer2);

        final Opt<String> opt3 = computeAnswer(84);
        final String answer3 = switch (opt3) { // D
            case Opt.Some<String> s -> s.value();
            case Opt.None __ -> "no answer";
        };
        System.out.println(answer3);
    }

    private static Opt<String> computeAnswer(final int question)
    {
        if (question % 2 == 0) {
            return Opt.some(String.valueOf(question / 2));
        } else {
            return Opt.none();
        }
    }

    private sealed interface Opt<T> permits Opt.Some, Opt.None
    {

        static <T> Opt<T> of(final T value)
        {
            return value == null ? none() : some(value);
        }

        static <T> Opt<T> some(final T value)
        {
            return new Opt.Some<>(value);
        }

        @SuppressWarnings("unchecked")
        static <T> Opt<T> none()
        {
            return Opt.None.NONE;
        }

        record Some<T>(T value) implements Opt<T>
        {

            public Some
            {
                Objects.requireNonNull(value, "Value must not be null");
            }
        }

        @SuppressWarnings({ "rawtypes" })
        enum None implements Opt
        {
            NONE;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

第一个 switch 表达式使用记录解构来使用变量的显式类型从 Some 变体中获取值,ans但在这种情况下,java 编译器需要默认分支(标有 A 的行),否则会失败并出现以下错误:

TestRecordPatter.java:[10,40] switch 表达式未涵盖所有可能的输入值

在第二个开关中,问题是使用var而不是显式类型 forans将开关的返回类型绑定到Object而不是绑定到String。(标有 B 的线)。此外,但这不是 java 编译器问题,IntelliJ 在标记为 C 的行上抱怨,突出显示该var ans部分并说“需要类型 T,但提供了 null”之类的内容。

最后,第三个开关工作正常,但这是“旧”(java 17 很旧,对吧?)的做法。

有人能帮我吗?难道我做错了什么?

编辑:我刚刚下载了 Oracle JDK-19 GA 版本,它也有同样的问题。

编辑2:第二个开关,具有与var第一个开关相同的行为,因此它需要一个默认情况,正如 Holger 所指出的

Bri*_*etz 6

难道我做错了什么?

是和不是。您正在对枚举和密封类型如何交互(以及常量case标签)进行假设,如果您来自 Haskell,那么这些假设是有意义的,但 Java 尚未赶上。具体来说,枚举的穷举性检查仅限于切换该枚举类型,因此当枚举类型用作密封类型的允许子类型时,枚举值的穷举性尚未被视为穷尽允许的子类型。这是因为常量大小写标签尚未被视为模式,而是以其旧含义对待。将来将会出现彻底混合恒定大小写标签和密封类型的情况(回想一下,这仍然是预览功能。)

与此同时,以下工作正常:

sealed interface Opt<T> { ... }

record Some<T>(T t) implements Opt<T> { }
record None<T>() implements Opt<T> { }
Run Code Online (Sandbox Code Playgroud)

我明白你为什么选择枚举,并且在时间成熟的情况下,这一举措会起作用,但目前还没有。


Msc*_*scG 0

我刚刚用JDK 20的编译器尝试了代码,问题已经解决