I have a java enum:
public enum PaxosRole {
FOLLOWER,
RECOVERER,
LEADER
}
I wish to use it in a record such as:
public record PaxosEvent(PaxosRole role, PaxosMessage message) {}
When I try to pattern match it with:
final var paxosEvent = new PaxosEvent(role, msg);
switch (paxosEvent){
case PaxosEvent(PaxosRole.FOLLOWER, var msg) -> {
// do something
}
case PaxosEvent(PaxosRole.RECOVERER, var msg) -> {
// do something else
}
case PaxosEvent(PaxosRole.LEADER, var msg) -> {
// do something distinct
}
}
My IDE says Unknown class
for the enum in the pattern matches.
Trying to swap the enum for a complex sealed class:
sealed interface PaxosRole {
record Follower() implements PaxosRole {};
record Recoverer() implements PaxosRole {};
record Leader() implements PaxosRole {};
Follower FOLLOWER = new Follower();
Recoverer RECOVERER = new Recoverer();
Leader LEADER = new Leader();
}
Then the following does not work:
switch (paxosEvent){
case PaxosEvent(PaxosRole.Follower _, var msg) -> {
throw new AssertionError("follower role not implemented");
}
case PaxosEvent(PaxosRole.Recoverer _, var msg) -> {
throw new AssertionError("follower role not implemented");
}
case PaxosEvent(PaxosRole.Leader _, var msg) -> {
throw new AssertionError("follower role not implemented");
}
}
My IDE says that only the first branch will match; the other two branches are the same class. I am guessing it thinks all are of type PaxosRole
. Even if it was to work, it looks pretty verbose.
I can use a with
to check the enum:
switch (paxosEvent){
case PaxosEvent(var role, var m) when role.equals(PaxosRole.FOLLOWER)-> {
throw new AssertionError("follower role not implemented");
}
case PaxosEvent(var role, var m) when role.equals(PaxosRole.RECOVERER)-> {
throw new AssertionError("follower role not implemented");
}
case PaxosEvent(var role, var m) when role.equals(PaxosRole.LEADER)-> {
throw new AssertionError("follower role not implemented");
}
}
Yet my IDE then tells me that the match is not exhaustive. So, it wants me to add a default branch. Yet it is exhaustive; all three enums are covered in the with
statements.
Is there a way to use a simple enum in a switch in Java 22 without a default and without being so verbose?
Mixing value cases and record/type patterns is not supported yet.
Your work-around
sealed interface PaxosRole {
record Follower() implements PaxosRole {};
record Recoverer() implements PaxosRole {};
record Leader() implements PaxosRole {};
Follower FOLLOWER = new Follower();
Recoverer RECOVERER = new Recoverer();
Leader LEADER = new Leader();
}
switch(paxosEvent){
case PaxosEvent(PaxosRole.Follower _, var msg) -> {
throw new AssertionError("follower role not implemented");
}
case PaxosEvent(PaxosRole.Recoverer _, var msg) -> {
throw new AssertionError("recoverer role not implemented");
}
case PaxosEvent(PaxosRole.Leader _, var msg) -> {
throw new AssertionError("leader role not implemented");
}
}
does work with javac
; any problems in your IDE are related to the IDE.
Alternatively, you can use record patterns instead of type patterns:
switch(paxosEvent) {
case PaxosEvent(PaxosRole.Follower(), var msg) -> {
throw new AssertionError("follower role not implemented");
}
case PaxosEvent(PaxosRole.Recoverer(), var msg) -> {
throw new AssertionError("recoverer role not implemented");
}
case PaxosEvent(PaxosRole.Leader(), var msg) -> {
throw new AssertionError("leader role not implemented");
}
}
But there is no need for pattern matching at all with your original definition of PaxosRole
as an enum
type. You could simply use
switch(paxosEvent.role()) {
case FOLLOWER -> {
throw new AssertionError("follower role not implemented");
}
case RECOVERER -> {
throw new AssertionError("recoverer role not implemented");
}
case LEADER -> {
throw new AssertionError("leader role not implemented");
}
}
This would require accessing paxosEvent.message()
manually when needed. If you really need to avoid that, you can nest switch statements:
switch(paxosEvent) {
case PaxosEvent(var role, var msg) -> {
switch(role) {
case FOLLOWER -> {
throw new AssertionError("follower role not implemented");
}
case RECOVERER -> {
throw new AssertionError("recoverer role not implemented");
}
case LEADER -> {
throw new AssertionError("leader role not implemented");
}
}
}
}
This can still be quite compact. E.g. as an expression:
int i = switch(paxosEvent) {
case PaxosEvent(var role, var msg) -> switch(role) {
case FOLLOWER -> 1;
case RECOVERER -> 2;
case LEADER -> 3;
};
};
And pattern matching will evolve. Patterns like in your first attempt might be possible in the future…