blob: 8cd3be544d44e1ae73c07c9509a9c7b2ddcb0c1e [file] [log] [blame] [view]
We're trying to make long chains of `if` statements clearer (and potentially
faster) by converting them into `switch`es.
### Long chains of `if` statements
* When a chain of `if ... else if ... else if ...` statements has `K` total
branches, at runtime one needs to check `O(K)` conditions on average
(assuming equal likelihood of each branch)
* Condition-checking expressions are often repeated multiple times, once in
each `if (...)`. Besides redundancy, this introduces a potential bug vector:
an `if` in the chain could unintentionally have a slightly different
condition than others, an ordering bug (see below), *etc.*
### `switch`es:
* Support constants (`1`, `2`, ...), `enum` values, `null`, and pattern
matching, including mixtures of these
* Reduces redundancy
* Runtime performance may benefit
### Examples
#### 1. Enum conversion
``` {.bad}
enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};
private void foo(Suit suit) {
if (suit == Suit.SPADE) {
System.out.println("spade");
} else if (suit == Suit.DIAMOND) {
System.out.println("diamond");
} else if (suit == Suit.HEART) {
System.out.println("heart);
} else if (suit == Suit.CLUB) {
System.out.println("club");
}
}
```
Which can be converted into:
``` {.good}
enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};
private void foo(Suit suit) {
switch (suit) {
case Suit.SPADE -> System.out.println("spade");
case Suit.DIAMOND -> System.out.println("diamond");
case Suit.HEART -> System.out.println("heart");
case Suit.CLUB -> System.out.println("club");
}
}
```
Note that with the new `switch` style (`->`), one gets exhaustiveness checking
"for free". That is, if a new `Suit` value were to be added to the `enum`, then
the `switch` would raise a compile-time error, whereas the original chain of
`if` statements would need to be manually detected and edited.
#### 2. Patterns
This conversion works for `instanceof`s too:
``` {.bad}
enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};
private void describeObject(Object obj) {
if (obj instanceof String) {
System.out.println("It's a string!");
} else if (obj instanceof Number n) {
System.out.println("It's a number!");
} else if (obj instanceof Object) {
System.out.println("It's an object!");
}
}
```
This can be converted as follows (if the `instanceof` does not originally have a
pattern variable, then `unused` will be inserted):
``` {.good}
enum Suit {HEARTS, CLUBS, SPADES, DIAMONDS};
private void describeObject(Object obj) {
switch(obj) {
case String unused -> System.out.println("It's a string!");
case Number n -> System.out.println("It's a number!");
case Object unused -> System.out.println("It's an object!");
}
}
```
In later Java versions, an unnamed variable (`_`) can be used in place of
`unused`.
#### 3. Ordering Bugs
With `if` chains, it's possible to write code such as:
``` {.bad}
private void describeObject(Object obj) {
if (obj instanceof Object) {
System.out.println("It's an object!");
} else if (obj instanceof Number n) {
System.out.println("It's a number!");
} else if (obj instanceof String) {
System.out.println("It's a string!");
}
}
```
When calling `describeObject("hello")`, one might expect to have `It's a
string!` printed, but this is not what happens. Because the `Object` check
happens first in code, it matches, resulting in `It's an object!`. This behavior
is most likely a bug, and can sometimes be hard to spot. (This check can be
suppressed if the behavior is intentional.)