# Overloading

Languages without support for default arguments often use overloading to imitate that feature.

The term overload refers to the name of a function: You use the same name (“overload” that name) for different functions as long as the parameter lists differ. Here, we overload the member function f():

// Overloading/Overloading.kt
package overloading
import atomictest.eq

class Overloading {
  fun f() = 0
  fun f(n: Int) = n + 2
}

fun main() {
  val o = Overloading()
  o.f() eq 0
  o.f(11) eq 13
}

In Overloading, you see two functions with the same name, f(). The function’s signature consists of the name, parameter list and return type. Kotlin distinguishes one function from another by comparing signatures. When overloading functions, the parameter lists must be unique—you cannot overload on return types.

The calls show that they are indeed different functions. A function signature also includes information about the enclosing class (or the receiver type, if it’s an extension function).

Note that if a class already has a member function with the same signature as an extension function, Kotlin prefers the member function. However, you can overload the member function with an extension function:

// Overloading/MemberVsExtension.kt
package overloading
import atomictest.eq

class My {
  fun foo() = 0
}

fun My.foo() = 1             // [1]

fun My.foo(i: Int) = i + 2   // [2]

fun main() {
  My().foo() eq 0
  My().foo(1) eq 3
}
  • [1] It’s senseless to declare an extension that duplicates a member, because it can never be called.
  • [2] You can overload a member function using an extension function by providing a different parameter list.

Don’t use overloading to imitate default arguments. That is, don’t do this:

// Overloading/WithoutDefaultArguments.kt
package withoutdefaultarguments
import atomictest.eq

fun f(n: Int) = n + 373
fun f() = f(0)

fun main() {
  f() eq 373
}

The function without parameters just calls the first function. The two functions can be replaced with a single function by using a default argument:

// Overloading/WithDefaultArguments.kt
package withdefaultarguments
import atomictest.eq

fun f(n: Int = 0) = n + 373

fun main() {
  f() eq 373
}

In both examples you can call the function either without an argument or by passing an integer value. Prefer the form in WithDefaultArguments.kt.

When using overloaded functions together with default arguments, calling the overloaded function searches for the “closest” match. In the following example, the foo() call in main() does not call the first version of the function using its default argument of 99, but instead calls the second version, the one without parameters:

// Overloading/OverloadedVsDefaultArg.kt
package overloadingvsdefaultargs
import atomictest.*

fun foo(n: Int = 99) = trace("foo-1-$n")

fun foo() {
  trace("foo-2")
  foo(14)
}

fun main() {
  foo()
  trace eq """
    foo-2
    foo-1-14
  """
}

You can never utilize the default argument of 99, because foo() always calls the second version of f().

Why is overloading useful? It allows you to express “variations on a theme” more clearly than if you were forced to use different function names. Suppose you want addition functions:

// Overloading/OverloadingAdd.kt
package overloading
import atomictest.eq

fun addInt(i: Int, j: Int) = i + j
fun addDouble(i: Double, j: Double) = i + j

fun add(i: Int, j: Int) = i + j
fun add(i: Double, j: Double) = i + j

fun main() {
  addInt(5, 6) eq add(5, 6)
  addDouble(56.23, 44.77) eq
    add(56.23, 44.77)
}

addInt() takes two Ints and returns an Int, while addDouble() takes two Doubles and returns a Double. Without overloading, you can’t just name the operation add(), so programmers typically conflate what with how to produce unique names (you can also create unique names using random characters but the typical pattern is to use meaningful information like parameter types). In contrast, the overloaded add() is much clearer.

  • -

The lack of overloading in a language is not a terrible hardship, but the feature provides valuable simplification, producing more readable code. With overloading, you just say what, which raises the level of abstraction and puts less mental load on the reader. If you want to know how, look at the parameters. Notice also that overloading reduces redundancy: If we must say addInt() and addDouble(), then we essentially repeat the parameter information in the function name.

Exercises and solutions can be found at www.AtomicKotlin.com.