Skift udtryk med ugyldig returtype
Måske give en Consumer
af Event
, så du giver noget nyttigt, er afvejningen en linje mere for 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);
Fortsæt, hvis du bekymrer dig om ydeevne
Baseret på kommentaren om præstationsstraf udføres et benchmark for at sammenligne følgende scenarier:
- Brug af forbruger og håndtering er instansmetode
- Brug af forbruger og håndtag er en statisk metode
- Ikke bruger forbruger og håndtere er instansmetode
- At bruge forbruger og håndtag er en statisk metode
At se
- Har brug af Consumer stor effekt på ydeevnen?
- Er der nogen forskel for statisk og instans
handle
metode?
Og resultatet er:
# Run complete. Total time: 00:08:08
Benchmark Mode Cnt Score Error Units
SwitchExpressionBenchMark.consumerHandle thrpt 20 5643.921 ± 79.075 ops/s
SwitchExpressionBenchMark.consumerStaticHandle thrpt 20 5549.207 ± 115.133 ops/s
SwitchExpressionBenchMark.noConsumerHandle thrpt 20 5616.466 ± 23.528 ops/s
SwitchExpressionBenchMark.noConsumerStaticHandle thrpt 20 5635.814 ± 7.611 ops/s
Ved at observere resultatet,
- Brug af Consumer har ikke stor effekt på ydeevnen.
- At bruge Consumer med statisk metode er hurtigere end instansmetoden, følg kommentaren fra Brian Goetz og Holger.
Benchmark udføres med:
CPU:Intel(R) Core(TM) i7-8750H
Hukommelse:16G
JMH version:1.19
VM-version:JDK 15.0.2
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Mode;
import java.util.function.Consumer;
public class SwitchExpressionBenchMark {
public static void main(String[] args) throws Exception {
org.openjdk.jmh.Main.main(args);
}
@Fork(value = 1, warmups = 2)
@BenchmarkMode(Mode.Throughput)
@Benchmark
public void consumerStaticHandle() {
Event event = new InvoiceEvent();
event.setEventType(EventType.INVOICE);
Consumer<Event> consumer = switch (event.getEventType()) {
case ORDER -> e -> staticHandle((OrderEvent) e);
case INVOICE -> e -> staticHandle((InvoiceEvent) e);
case PAYMENT -> e -> staticHandle((PaymentEvent) e);
};
consumer.accept(event);
}
@Fork(value = 1, warmups = 2)
@BenchmarkMode(Mode.Throughput)
@Benchmark
public void consumerHandle() {
Event event = new InvoiceEvent();
event.setEventType(EventType.INVOICE);
Consumer<Event> consumer = switch (event.getEventType()) {
case ORDER -> e -> this.handle((OrderEvent) e);
case INVOICE -> e -> this.handle((InvoiceEvent) e);
case PAYMENT -> e -> this.handle((PaymentEvent) e);
};
consumer.accept(event);
}
@Fork(value = 1, warmups = 2)
@BenchmarkMode(Mode.Throughput)
@Benchmark
public void noConsumerHandle() {
Event event = new InvoiceEvent();
event.setEventType(EventType.INVOICE);
int unused = switch (event.getEventType()) {
case ORDER -> {
this.handle((OrderEvent) event);
yield 0;
}
case INVOICE -> {
this.handle((InvoiceEvent) event);
yield 0;
}
case PAYMENT -> {
this.handle((PaymentEvent) event);
yield 0;
}
};
}
@Fork(value = 1, warmups = 2)
@BenchmarkMode(Mode.Throughput)
@Benchmark
public void noConsumerStaticHandle() {
Event event = new InvoiceEvent();
event.setEventType(EventType.INVOICE);
int unused = switch (event.getEventType()) {
case ORDER -> {
staticHandle((OrderEvent) event);
yield 0;
}
case INVOICE -> {
staticHandle((InvoiceEvent) event);
yield 0;
}
case PAYMENT -> {
staticHandle((PaymentEvent) event);
yield 0;
}
};
}
private static void staticHandle(PaymentEvent event) {
doSomeJob();
}
private static void staticHandle(InvoiceEvent event) {
doSomeJob();
}
private static void staticHandle(OrderEvent event) {
doSomeJob();
}
private void handle(PaymentEvent event) {
doSomeJob();
}
private void handle(InvoiceEvent event) {
doSomeJob();
}
private void handle(OrderEvent event) {
doSomeJob();
}
private static double doSomeJob() {
double d = 0;
for (int i = 0; i < 10000; i++) {
d += Math.pow(Math.PI, i);
}
return d;
}
private enum EventType {
ORDER, INVOICE, PAYMENT
}
private static class Event {
public EventType getEventType() {
return eventType;
}
public void setEventType(EventType eventType) {
this.eventType = eventType;
}
private EventType eventType;
}
private static class OrderEvent extends Event {
}
private static class InvoiceEvent extends Event {
}
private static class PaymentEvent extends Event {
}
}
Stillingen af spørgsmålet er lidt af et "XY-problem"; det, du ønsker, er totalitetskontrol, men du beder om, at det skal behandles som et udtryk, ikke fordi du vil have et udtryk, men fordi du vil have den helhedskontrol, der følger med expression-hood.
En af de elementer af "teknisk gæld", der er tilbage fra tilføjelsen af skifteudtryk, er muligheden for skifte udsagn at tilmelde sig den samme helhedskontrol, som skifteudtryk får. Vi kunne ikke med tilbagevirkende kraft ændre dette om switch-sætninger -- switch-udsagn har altid fået lov til at være delvise -- men du har ret i, at det ville være rart at kunne få denne form for typekontrol. Som du antager, er det en måde at komme dertil at omdanne den til en void expression switch, men den er faktisk grim, og endnu værre, vil den ikke være let at finde. Det er på vores liste at finde en måde, hvorpå du kan vælge tilbage til totalkontrol for switch-udsagn. Der har været diskussioner om amber-spec-experts
liste over dette; det er relateret til flere andre mulige funktioner, og designdiskussioner er stadig i gang.
Hvis du har testklasser (f.eks. JUNIT-testcases), som du bygger og kører, før du frigiver din hovedkode, så kan du droppe en simpel vagtfunktion i enhver eksisterende testklasse for hver enum, du vil se:
String checkForEnumChanged(YourEnum guard) {
return switch (guard) {
case ORDER -> "OK";
case INVOICE -> "OK";
case PAYMENT -> "OK";
};
}
Dette betyder, at du kan holde din hovedapplikationskode fri af yield 0;
switch-stil og få en kompileringsfejl i testklasserne, når enum-værdierne redigeres.