首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java 19编译器在尝试开关表达式中的记录模式时出现问题

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

Stack Overflow用户
提问于 2022-09-20 13:57:25
回答 3查看 243关注 0票数 3

我从这里下载了JDK19的发布候选版本https://jdk.java.net/19/,以便使用在那里实现的新记录模式,但我遇到了一些问题。在我的测试中,我以以下方式编写了基于密封接口和记录的可选版本:

代码语言:javascript
复制
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;
        }
    }
}

第一个开关表达式是使用记录解构来使用ans变量的显式类型从某个变量中获取值,但在本例中,java编译器需要默认分支(标记为A的行),否则它将失败,并出现以下错误:

TestRecordPatter.java:10,40开关表达式并不涵盖所有可能的输入值。

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

最后,第三个开关工作正常,但这是“旧”(java 17是旧的,对吗?)这样做的方式。

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

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

编辑2:第二个开关(带有var的开关)具有与第一个开关相同的行为,所以它需要一个默认的情况,正如霍格所指出的

EN

回答 3

Stack Overflow用户

发布于 2022-09-20 16:32:02

我做错了什么吗?

是也不是。您在假设枚举和密封类型是如何相互作用的(以及常量的case标签),如果您来自Haskell,但是Java还没有解决这些问题。具体来说,枚举的穷竭性检查仅限于对该枚举类型的切换,因此,当枚举类型被用作密封类型的允许子类型时,枚举值的穷竭性尚未被视为耗尽允许的子类型。这是因为不变的案例标签还没有被视为模式,而是它们的旧含义。将固定的case标签和密封类型混合在一起是未来的事情(回想一下,这仍然是一个预览功能)。

同时,以下功能也很好:

代码语言:javascript
复制
sealed interface Opt<T> { ... }

record Some<T>(T t) implements Opt<T> { }
record None<T>() implements Opt<T> { }

我明白你为什么要去拿墓园,而且在充分的时间里,这一举动会奏效,但它还没有实现。

票数 3
EN

Stack Overflow用户

发布于 2022-09-21 14:26:20

enum型是一种红鲱鱼。无论我们在第二种情况下使用enum类型还是使用解构模式的record类型,行为都是完全相同的。原因是有问题的情况是第一个。

考虑一下

代码语言:javascript
复制
import java.util.Objects;

public class TestRecordPattern2 {
    public static void main(final String[] args) {
        for(int i = 0; i < 4; i++) {
            final Opt<String> opt = computeAnswer(i);
            final String answer = switch(opt) {
                case Opt.Some<String>(String ans) -> ans;
                case Opt.None<String>() -> "no answer";
            };
            System.out.println(answer);
        }
    }
    private static Opt<String> computeAnswer(final int question) {
        return question % 2 == 0? Opt.some(String.valueOf(question / 2)): Opt.none();
    }
    private sealed interface Opt<T> {
        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<T>)Opt.None.NONE;
        }
        record Some<T>(T value) implements Opt<T> {
            public Some { Objects.requireNonNull(value, "Value must not be null"); }
        }
        record None<T>() implements Opt<T> {
            static final None<?> NONE = new None<>();
        }
    }
}

这就产生了问题,问题也是如此。

代码语言:javascript
复制
import java.util.Objects;

public class TestRecordPattern3 {
    public static void main(final String[] args) {
        for(int i = 0; i < 4; i++) {
            final Opt<String> opt = computeAnswer(i);
            final String answer = switch(opt) {
                case Opt.Some<String>(String ans) -> ans;
                case Opt.None __ -> "no answer";
            };
            System.out.println(answer);
        }
    }
    private static Opt<String> computeAnswer(final int question) {
        return question % 2 == 0? Opt.some(String.valueOf(question / 2)): Opt.none();
    }
    private sealed interface Opt<T> {
        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<T>)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 }
    }
}

但是,如果我们改变了路线

代码语言:javascript
复制
  case Opt.Some<String>(String ans) -> ans;

代码语言:javascript
复制
  case Opt.Some<String>(Object ans) -> ans.toString();

这两种变体的工作都没有问题。看来,耗尽性测试在这里的record组件的类型上有问题。

我们也可以用

代码语言:javascript
复制
final String answer = switch(opt) {
    case Opt.Some<String>(String ans) -> ans;
    case Opt.Some<String>(Object ans) -> throw new IllegalStateException("heap pollution");
    case Opt.None<String>() -> "no answer";
};

resp.

代码语言:javascript
复制
final String answer = switch(opt) {
    case Opt.Some<String>(String ans) -> ans;
    case Opt.Some<String>(Object ans) -> throw new IllegalStateException("heap pollution");
    case Opt.None __ -> "no answer";
};

对于enum变体。

尽管这对于一个健全的泛型类型系统来说不应该是必要的。

这也与JEP 405的“记录模式与详尽切换”一节相矛盾,它展示了说明这种穷竭性检查应该有效的例子。

出于好奇,我在你的案例中加入了JEP的例子。当我用

代码语言:javascript
复制
    public static void main(final String[] args) {
        final Opt.Some<I> opt = (Opt.Some<I>)Opt.<I>some(new A());
        final String answer = switch(opt) {
            case Opt.Some<I>(A ans) -> "A";
            case Opt.Some<I>(B ans) -> "B";
        };
        System.out.println(answer);
    }

    private sealed interface I {}
    static final class A implements I {}
    static final class B implements I {}

用之不竭的测试确实有效。但是,当我把它扩展到

代码语言:javascript
复制
    public static void main(final String[] args) {
        final Opt<I> opt = Opt.some(new A());
        final String answer = switch(opt) {
            case Opt.Some<I>(A ans) -> "A";
            case Opt.Some<I>(B ans) -> "B";
            case Opt.None<I>() -> "None";
        };
        System.out.println(answer);
    }

它再次未能认识到耗尽性,需要添加一个实际上不可能的Object情况,如

代码语言:javascript
复制
    public static void main(final String[] args) {
        final Opt<I> opt = Opt.some(new A());
        final String answer = switch(opt) {
            case Opt.Some<I>(A ans) -> "A";
            case Opt.Some<I>(B ans) -> "B";
            case Opt.Some<I>(Object ans) -> throw new AssertionError();
            case Opt.None<I>() -> "None";
        };
        System.out.println(answer);
    }

(甚至连case Opt.Some<I>(I ans) -> …都不起作用,就像使用String示例那样)

最后,最简化的示例是

代码语言:javascript
复制
public class TestRecordPattern5 {
    public static void main(final String[] args) {
        final Opt<String> opt = new Some<>("hello");
        final String answer = switch(opt) {
            case Some<String>(String ans) -> ans;
        };
        System.out.println(answer);
    }
    private sealed interface Opt<T> {}
    record Some<T>(T value) implements Opt<T> {}
}
票数 1
EN

Stack Overflow用户

发布于 2022-09-21 07:27:41

正如我在上面的注释中所写的,我试图用Brian的建议修改代码,但是第一个开关仍然没有通过完整性检查。我尝试过的代码如下

代码语言:javascript
复制
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<String> __ -> "no answer";
            // default -> throw new IllegalStateException("This should not happen"); // A
        };
        System.out.println(answer1);
    }

    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);
        }

        static <T> Opt<T> none()
        {
            return new Opt.None<>();
        }

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

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

        record None<T>() implements Opt<T> {}
    }
}

使其编译的唯一方法是对标记为A的行进行反编译。

编译器输出如下:

代码语言:javascript
复制
$ javac --enable-preview --release 19 tests/patterns/TestRecordPatter.java
tests/patterns/TestRecordPatter.java:10: error: the switch expression does not cover all possible input values
                final String answer1 = switch (opt1) {
                                       ^
Note: tests/patterns/TestRecordPatter.java uses preview features of Java SE 19.
Note: Recompile with -Xlint:preview for details.
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73787918

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档