@miensol provides a good answer addressing the lower-level details of using a singleton empty list object, minimizing allocations. In my opinion, the allocation of an empty list in JVM-based language is only marginally significant. You will be losing a few bytes of memory or wasting only a few clock cycles when instantiating via `listOf()`. Of course, if you create empty lists all of the time, this may add up, but it's difficult for me to see where this is applicable.
While I do not know the design intent of the API, I definitely see a ***semantic*** reason for its inclusion.
As @miensol already stated, `emptyList()` appears like a good candidate for a default parameter. The reason this is true is because it adheres to the well-known [Null-Object Pattern](
[To see links please register here]
).
Briefly, the pattern suggests a design where we use semantically meaningful types to represent our empty, default, or null values. In languages susceptible to null-pointer exceptions, this is useful because a proper object allows the client to call any of the available class members.
Consider the example below, where we use a default of `null`.
<!-- language:kotlin -->
class AcademicProfile(val courseGradesAsPercent : List<Double>? = null) {
fun displayAverage() {
if (courseGradesAsPercent == null)
println("No average to calculate, please sign-up for a course!")
else {
val sum = courseGradesAsPercent.reduce { accumulator, value -> accumulator + value }
val average = sum / courseGradesAsPercent.count()
println("Your unweighted average is $average")
}
}
}
The intent expressed by this class is that the list of course grades can be in any of the following states:
- null
- not-null, but empty
- filled with elements
Now, suppose we used `emptyList()` instead as the default:
<!-- language:kotlin -->
class AcademicProfile(val courseGradesAsPercent : List<Double> = emptyList()) {
fun displayAverage() {
if (courseGradesAsPercent.isEmpty())
println("No average to calculate, please sign-up for a course!")
else {
val sum = courseGradesAsPercent.reduce { accumulator, value -> accumulator + value }
val average = sum / courseGradesAsPercent.count()
println("Your unweighted average is $average")
}
}
}
In this scenario, there is no confusion between `null` and empty. If there are no grades, then the list is empty. These semantics are carried into the function by allowing us to call a member on the null-object's type (i.e. `List<Double>.isEmpty()`). Working with `null` expresses less about the domain and instead relies on the language's mechanics.
The performance benefit of using the singleton empty list is just icing on the cake!