Learn Kotlin Programming(Second Edition)
上QQ阅读APP看书,第一时间看更新

Infix functions

An infix function is one where an operator or function name is placed between the operands or arguments. An example in Kotlin is the to function, which is used to create a pair instance:

    val pair = "London" to "UK"

In Kotlin, member functions can be defined as an infix; this allows them to be used in this way. Since an infix function is placed between two arguments, all infix functions must operate on two parameters. The first parameter is the instance that the function is invoked on. The second parameter is an explicit parameter to the function.

To define your own infix function, use the infix keyword before the fun keyword, remembering that infix functions have only one explicit parameter. For example, to create a function called concat, which appends two strings, we would use the following code snippet:

    infix fun concat(other:String): String { 
      return this + other 
    } 

Then, we can invoke this function like this:

"hello" concat "world"

For instance, we may want to model a bank account class, which would contain a balance. In this class, we would most likely want some kind of function that adds to the customer's balance:

    class Account { 
      var balance = 0.0 
 
      fun add(amount: Double): Unit { 
        this.balance = balance + amount 
      } 
    } 

To use this, we could invoke it by using the regular dot syntax:

    val account = Account() 
    account.add(100.00) 

However, we could use this as an infix function if we wish to add the infix keyword to the function definition:

    class InfixAccount { 
      var balance = 0.0 
 
      infix fun add(amount: Double): Unit { 
        this.balance = balance + amount 
      } 
    }

Then, we could invoke it like an operator in the infix style:

    val account2 = InfixAccount() 
    account2 add 100.00 

In this example, there is not much difference between the readability of either style. Therefore, you would likely settle on the standard dot notation. However, there are occasions when infix functions can be a benefit.

An example of such a case is for short-named, frequently used functions, such as the to function that exists in the Kotlin standard library. The to function is an extension function on all types (defined on Any). It is used to create a Pair instance. If you recall from the Extension functions section, a Pair type is a simple wrapper for two values.

As useful as the Pair type is, when instantiated directly, it can add noise to the two values. Compare the following equivalent pieces of code and see what you think is more readable:

    val pair1 = Pair("london", "paris") 
    val pair2 = "london" to "paris" 

The second is less verbose, and, in many cases, more readable. This particular function is very useful when creating map literals. Again, compare the following two styles:

    val map1 = mapOf(Pair("London", "UK"), Pair("Bucharest",  "Romania")) 
    val map2 = mapOf("London" to "UK", "Bucharest" to "Romania") 

Other good examples of infix functions include bitwise operations (see the Basic types in Kotlin section in Chapter 2, Kotlin Basics) and custom domain-specific language (DSLs).

One custom DSL that benefits from the infix operations is in the KotlinTest testing framework. This framework uses infix functions so that assertions in tests can be written in natural language. For example, refer to the following:

    myList should contain(x) 
    myString should startWith("foo") 

KotlinTest and the testing DSL will be covered in depth in Chapter 11, Testing in Kotlin.