The idea
Code can be correct and still carry extra weight. One common kind of extra weight is an else that does nothing a plain line after the if couldn't do. Removing it makes the code shorter and flatter, with the same behaviour.
The starting code of the lesson is a function that always returns a value that is never positive:
class Solution {
fun negative(n: Int): Int {
if (n < 0) {
return n
} else {
return -n
}
}
}
It works. For a negative n it returns n and for anything else it returns -n. But look at what return does: it ends the function immediately. So by the time execution could reach anything after the first return, the function has already finished.
Why the else isn't needed
Follow the two cases.
- When
n < 0is true, the first block runs,return nexecutes and the function ends. No later line is reached. - When
n < 0is false, the block is skipped and execution continues with the next line after theifblock.
So the code after the if already runs only when the condition was false. That is exactly what else promises, so writing else says the same thing twice. The word and its pair of braces add nothing.
The exercise
Remove the redundant part without changing what the function returns. Take out the else keyword, and with it the braces that opened and closed the branch. The if keeps its own closing brace, and the return -n moves out to the level of the if:
class Solution {
fun negative(n: Int): Int {
if (n < 0) {
return n
}
return -n
}
}
The behaviour is identical: negative(4) returns -4, negative(-4) returns -4 and negative(0) returns 0. Two lines and a level of nesting are gone.
Reading the cleaned-up version aloud gives a natural sentence: if n is below zero, give it back as it is; otherwise, give back its negation.
Where this matters more
With two branches the saving is small. With three or more outcomes, the effect on readability is large. This version uses else after every return:
fun grade(score: Int): String {
if (score < 50) {
return "fail"
} else if (score < 80) {
return "pass"
} else {
return "distinction"
}
}
And this version has no else at all:
fun grade(score: Int): String {
if (score < 50) {
return "fail"
}
if (score < 80) {
return "pass"
}
return "distinction"
}
Both give fail for 10, pass for 60 and distinction for 90. The second reads top to bottom as a list of special cases, each one handled and finished, followed by the general case.
When you must keep the else
The trick depends on the earlier branch really leaving the function. If a branch only prints, or assigns a variable, execution continues past the if block, and removing the else changes the behaviour:
if (n < 0) {
println("negative")
}
println("positive")
For n = -2 this prints both lines. The original program, with the second println inside an else, printed only one. Before deleting an else, check that every path through the if block ends in return, or in something else that leaves, such as throw.
Common mistakes
- Removing the
elsebut keeping its braces. In Kotlin, a bare{ ... }after anifblock is not a plain block. It is read as a lambda, soreturninside it is rejected ("'return' is prohibited here") and the function reports a missing return. Remove the braces together with theelse. - Dropping the final
return. A function declared as returningIntneeds a return on every path. With theelsegone, the closingreturn -nis what covers the case where the condition is false. - Changing the logic while tidying. The cleanup should not alter the condition. Run the same examples before and after.
Key takeaways
returnends the function, so code after anifthat returns only runs when the condition was false.- In that situation,
elseis redundant and can be removed. - Flatter code with early returns is easier to read, especially with several cases.
- Keep the
elsewhenever theifblock can finish without leaving the function.