返回类型为void的switch表达式
当 switch 分支调用具有 void 返回类型的方法时,有没有办法强制对所有枚举值进行彻底检查?仅仅为了诱使编译器要求穷举而对产量进行硬编码是非常丑陋的。
这是我当前的模式(句柄方法具有 void 返回类型)
int unused = switch (event.getEventType()) {
case ORDER -> { handle((OrderEvent) event); yield 0; }
case INVOICE -> { handle((InvoiceEvent) event); yield 0; }
case PAYMENT -> { handle((PaymentEvent) event); yield 0; }
};
我想使用表达式的原因是在添加新枚举值但未处理时出现编译错误。
回答
也许 yield a Consumerof Event,所以你产生了一些有用的东西,权衡是多一行consumer.accept。
Consumer<Event> consumer = switch (event.getEventType()) {
case ORDER -> e -> handle((OrderEvent) e);
case INVOICE -> e -> handle((InvoiceEvent) e);
case PAYMENT -> e -> handle((PaymentEvent) e);
};
consumer.accept(event);
Consumer<Event> consumer = switch (event.getEventType()) {
case ORDER -> e -> handle((OrderEvent) e);
case INVOICE -> e -> handle((InvoiceEvent) e);
case PAYMENT -> e -> handle((PaymentEvent) e);
};
consumer.accept(event);
如果您关心性能,请继续
根据有关性能损失的评论,执行基准测试以比较以下场景:
- 使用消费者和句柄是实例方法
- 使用消费者和句柄是静态方法
- 不使用消费者和句柄是实例方法
- 不使用消费者和句柄是静态方法
查看
- 使用 Consumer 对性能有很大影响吗?
- 静态
handle方法和实例方法有什么区别吗?
结果是:
# Run complete. Total time: 00:20:30
Benchmark Mode Cnt Score Error Units
SwitchExpressionBenchMark.consumerHandle thrpt 300 49343.496 ± 91.324 ops/ms
SwitchExpressionBenchMark.consumerStaticHandle thrpt 300 49312.273 ± 112.630 ops/ms
SwitchExpressionBenchMark.noConsumerHandle thrpt 300 49353.232 ± 106.522 ops/ms
SwitchExpressionBenchMark.noConsumerStaticHandle thrpt 300 49496.614 ± 122.916 ops/ms
通过观察结果,4种场景之间没有太大区别。
- 使用 Consumer 不会对性能产生重大影响。
- 静态
handle方法和实例方法之间的性能差异是可以忽略的。
基准测试使用:
CPU:Intel(R) Core(TM) i7-8750H
内存:16G
JMH 版本:1.19
VM 版本:JDK 15.0.2
- @BrianGoetz only when those `handle` methods are `static`
- @ArborealShark All of the lambdas in this particular example are non-capturing, and therefore instances would be memoized and cached at the capture site, with zero performance overhead.
- Am I wrong to assume this solution has a negative performance impact by allocating a lambda function during each execution? If the switch expression is in a critical hot path it may be significant.
- Note that the differences of the Score values are all in the order of magnitude of the reported Error, so the actual conclusion is that they are basically all the same. Or that a better test setup is needed.
- The fact that `doSomeJob()`’s return value is not used may affect the result (use JMH’s black hole to consume the value). Besides that, you may try with different parameters, e.g. warmup, to see whether they have an impact on the result. If that all doesn’t change the results, it may simply be the case the the approaches do not differ significantly.
回答
问题的陈述有点像“XY问题”;您想要的是完整性检查,但您要求将其视为表达式,不是因为您想要表达式,而是因为您想要表达式罩附带的完整性检查。
添加 switch 表达式后留下的“技术债务”项目之一是 switch语句能够选择与 switch 表达式相同的总体检查。我们无法追溯更改关于 switch 语句的这一点——switch 语句总是被允许是部分的——但是你是对的,能够进行这种类型检查是很好的。正如您所猜测的那样,将它变成一个无效的表情开关是一种方法,但它确实很丑,更糟糕的是,不容易被发现。在我们的列表中,可以找到一种方法来允许您选择重新进行 switch 语句的总体检查。已经在amber-spec-experts名单上讨论过这个问题;它与其他几个可能的功能有关,设计讨论仍在进行中。
回答
如果您有在发布主代码之前构建和运行的测试类(比如 JUNIT 测试用例),那么您可以将一个简单的保护函数放入任何现有的测试类中,以查看您想要查看的每个枚举:
String checkForEnumChanged(YourEnum guard) {
return switch (guard) {
case ORDER -> "OK";
case INVOICE -> "OK";
case PAYMENT -> "OK";
};
}
这意味着yield 0;当编辑枚举值时,您可以使您的主应用程序代码远离switch 样式,并在测试类中得到编译错误。
- @Naman This code is not intended for use in the OP application, it is there to cause compile fail in the test code which alerts OP of need to handle the enum change in non-yield switches used in main codebase.