Kotlin Functions - mariamaged/Java-Android-Kotlin GitHub Wiki

Kotlin - Functions

Trivial Functions

A function that does not require any parameters.

fun trivial() {
	println(1 + 1)
}

trivial()

Functions with Parameters

fun lessTrivial(left: Int, right: Int) {
	println(left + right)
}
  • Parameters are a comma delimited list of paired values:
    • The name of the parameter.
    • And its type.

Functions with Return Types

The functions shown so far are Kotlin's equivalent of Java methods that return void, in that they are not returning any values to the caller.

  • However, as with functions and methods in most programming languages, Kotlin functions can return values, using the return keyword.
  • However, to be able to return a value, the functions needs to declare the type of what is being returned.
  • In Kotlin, it is declared at the end, after the closing parentheses, separated by a colon.
fun simpleReturn(left: Int, right: Int) :Int {
return left + right;
}

val result = simpleReturn(1, 1);
println(result)

Local Variables

  • Variables declared inside a function are considered to be local variables.
    • They are available inside of the function but not outside of it.
    • And those variables go "out of scope" once the function returns.
  • So, we could rework our function to use a local variable to hold into the calculation:
fun actLocally(left: Int, right: Int) :Int {
val result = left + right;

return result;
}

println(actLocally(1, 1))

Fancier Functions

Expression Bodies

Frequently, our function bodies are lines of code wrapped in braces, as shown above.

  • Sometimes, you will encounter functions that are declared using an = instead.
  • It is designed to simplify cases where the entire method implementation is a single expression, such as adding two numbers together.
  • Here, we replace:
    • A pair of braces.
    • And a return keyword.
      with an =, which makes this code less verbose.
fun expressionBody(left: int, right: Int)  = left + right
println(expressionBody(1, 1))

Expression Bodies and Types

  • Another thing that we eliminated in this example is the return type.
  • We did not need to declare that expressionBody() returns an Int, as the compiler can determine that on its own.
    • It knows what the type of left is.
    • And it knows what the type of right is.
    • It even knows what the type of left + right is.
    • So, it knows the type of expressionBody() as a result.
       
  • However, sometimes, you will need to override that type.
  • For example, there will be cases where the expression evaluates to one type, but you want the function to return a supertype.
  • In those cases, you just add the : syntax for declaring the return type:
fun expressionType(left: int, right, Int) :Number = left + right;
println(expressionType(1, 1));

Why Bother?

  • In the end, this may not seem like a "big win", for two reasons:
    1. We are only saving a few characters.
    2. Not that many functions would appear to be simple enough that their entire implementation can be a single expression.

First Argument
  • The first argument is true: we are not saving much on a per-function basis.
  • However, these simplifications add up over time.
  • A lot of focus in Kotlin is on offering these small simplifications, in a lot of places, which combine to make Kotlin code much more concise than the equivalent code in Java and other languages.

Second Argument
  • It turns out that a lot more things in Kotlin can be written in terms of expressions than you might think.

Default Parameters Values

  • One annoyance in Java is that it lacks the concept of default parameter values that you have in other languages.
JavaScript Example
function increment(base, amount = 1) {
// something yummy
}
Ruby Example
def increment(base, amount = 1) 
	# something yummy
end	
Kotlin
  • Here, we call increment() with either one or two values.
    • If we only supply two values, then the caller controls base and amount.
    • If we only supply one value, the default value of 1 will be used for amount.
fun increment(base: Int, amount: Int = 1) = base + amount
println(increment(1)) // Prints 2.

Named Parameters

Functions with a lot of default values are often called using named parameters.

  • In the form of a call,
    • Rather than identifying parameters by the order in which they appear in the call.
    • You explicitly state the name of the parameter in the call itself.
fun increment(base:Int, amount: Int = 1) = base + amount

println(increment(base = 1))
println(increment(amount = 10, base = 1))
  • You can even "mix and match" positional parameters and names parameters.
fun increment(base: Int, amount: Int = 1) = base + amount

println(increment(1, amount = 10))
  • In this particular example, we are not gaining much from this syntax.
  • However, complex functions with lots of default parameter values can gain a lot from this.
  • Here, six of the parameters have default values defined, and so they are optional when we call iCanHazCookie().
fun iCanHazCookie(name: String,
		  value: String,
		  maxAge: Long? = null,
		  expires: LocalDateTime? = null,
		  domain: String? = null,
		  path: String? = null,
		  secure: Boolean = false,
		  httpOnly: Boolean = false) : String {
// Rest of code for creating a cookie header.
}

Positional Parameters Scenario:

  • Suppose that I want to create a cookie with a particular path, but I am willing to accept the defaults for the remaining five optional parameters.
  • If we were struck with positional parameters, we would have to call it like this.
  • In effect, the first three optional parameters are no longer optional, because we need them as placeholders.
val cookieHeader = iCanHazCookie("foo", "bar", null, null, null, "/");

Named parameters scenario:

// First option.
val cookieHeader = icanHazCookie(name = "foo", value = "bar", path = "/");

// Second option.
val cookieHeader = icanHazCookie("foo", "bar", path = "/");