The benefits of enums (as these are referred to in some languages) is that after the safe downcast you have access to the fields and methods of the specific type just by invoking them.
Pattern matching also makes this construct even nicer to use (and I would argue that having one without the other is worse than not having either), but that doesn't seem to be included in the language yet, very probably for good reasons (I just don't follow Java development closely so I don't know about them).
public int countNodes(JsonElement el) {
return switch(el) {
case JsonObject obj ->
obj.entrySet().stream()
.mapToInt(e -> countNodes(e.getValue())).sum()
case JsonArray arr ->
arr.stream().mapToInt(::countNodes).sum()
default -> 1
}
}
[0] https://www.javadoc.io/doc/com.google.code.gson/gson/latest/...Where are these used? Well, they are used in a sense all over the Java language already in the form of methods with checked exceptions, which say in their contract that they return either a successful result or any of the declared errors but nothing else.