我从这里下载了JDK19的发布候选版本https://jdk.java.net/19/,以便使用在那里实现的新记录模式,但我遇到了一些问题。在我的测试中,我以以下方式编写了基于密封接口和记录的可选版本:
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的开关)具有与第一个开关相同的行为,所以它需要一个默认的情况,正如霍格所指出的
发布于 2022-09-20 16:32:02
我做错了什么吗?
是也不是。您在假设枚举和密封类型是如何相互作用的(以及常量的case标签),如果您来自Haskell,但是Java还没有解决这些问题。具体来说,枚举的穷竭性检查仅限于对该枚举类型的切换,因此,当枚举类型被用作密封类型的允许子类型时,枚举值的穷竭性尚未被视为耗尽允许的子类型。这是因为不变的案例标签还没有被视为模式,而是它们的旧含义。将固定的case标签和密封类型混合在一起是未来的事情(回想一下,这仍然是一个预览功能)。
同时,以下功能也很好:
sealed interface Opt<T> { ... }
record Some<T>(T t) implements Opt<T> { }
record None<T>() implements Opt<T> { }我明白你为什么要去拿墓园,而且在充分的时间里,这一举动会奏效,但它还没有实现。
发布于 2022-09-21 14:26:20
enum型是一种红鲱鱼。无论我们在第二种情况下使用enum类型还是使用解构模式的record类型,行为都是完全相同的。原因是有问题的情况是第一个。
考虑一下
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<>();
}
}
}这就产生了问题,问题也是如此。
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 }
}
}但是,如果我们改变了路线
case Opt.Some<String>(String ans) -> ans;至
case Opt.Some<String>(Object ans) -> ans.toString();这两种变体的工作都没有问题。看来,耗尽性测试在这里的record组件的类型上有问题。
我们也可以用
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.
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的例子。当我用
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 {}用之不竭的测试确实有效。但是,当我把它扩展到
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情况,如
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示例那样)
最后,最简化的示例是
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> {}
}发布于 2022-09-21 07:27:41
正如我在上面的注释中所写的,我试图用Brian的建议修改代码,但是第一个开关仍然没有通过完整性检查。我尝试过的代码如下
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的行进行反编译。
编译器输出如下:
$ 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.https://stackoverflow.com/questions/73787918
复制相似问题