From Java to Kotlin - Standard Library Functions

Kotlin provides a standard library that is meant to augment the java’s standard library.

Apply

It makes code that needs to initialize an instance more readable.

It accepts a lambda that is invoked with the receiver being the instance where apply was called and
returns the original instances.

// without apply
val task = Runnable { print(Running) }
val thread = Thread(task)
thread.setDaemon(true)
thread.start()

// using apply
val task = Runnable { print(Running) }
Thread(task).apply { setDaemon(true) }.start()

Let

The let function makes it easy to deal with a nullable argument that should be passed to a function that expects a non-null parameter.

// you can't pass a value of a nullable type to this function
fun sendEmailTo(email:String) { }

// without let
if(email != null) {
  sendEmailTo(email)  
}

// using let. The function will only be called if non-null
email?.let { email -> sendEmailTo(email) }

With

It allows you to call multiple methods on the same object without repeating the reference to that object

// without with
//    this calls several methods on the result instance
fun alphabet(): String {
  val result = StringBuilder()

  for (letter in 'A'..'Z') {
    result.append(letter)
  }

  result.append("\nNow i know the alphabet!")
  return result.toString()
}

// using with
fun alphabet() = with(StringBuilder()) {
  for (letter in 'A'..'Z') {
    append(letter)
  }

  append("\nNow i know the alphabet!")
  toString()
}

Run

Run combines the use cases of with and let. A closure is passed to run, which has the instance as the receiver. The return value of the closure is used as the return value of run itself.

The key difference between let and run is that with run, the receiver is the instance. Where as in let, the argument to the closure is the instance.

val outputPath = Paths.get("/user/home").run {
  val path = resolve("output")
  path.toFile().createNewFile()
  path
}

Lazy

Lazy wraps an expensive function call to be invoked when first required.

If the value is requested twice, Kotlin will safely handle any race conditions by only executing the underlying function once.

// expensive operation declaration
fun readStringFromDatabase(): String = ..

// call
val lazyString = lazy { readStringFromDatabase() }

Use

Use is similar to try-with-resources from Java. It’s defined as an extension on an instance of closeable and accepts a function literal that operates on this closeable.

val input = Files.newInputStream(Paths.get("input.txt"))
val byte = inpute.use({ input.read() })

Repeat

It will invoke a literal n number of times.

repeat(times = 10, { println("Hello")} )

Require, Assert, Check

They’re assertions that should always hold true or false at the location when this assertions are executed. They key difference between them three is in the type of exception that’s raised.

Require throws an exception and it’s used to ensure that arguments match the input conditions
Assert throws an AssertionException and it’s used to ensure that our internal state is consistent. It’s the only one that can be disabled at runtime.
Check throws an IllegalStateException and it’s also used for internal state consistency

fun neverEmpty(str: String) {
  require(str.length > 0, { "String should not be empty" })
  println(str)
}

The function literal that’s passed as a message to the functions is lazily evaluated. It won’t be invoked if the condition holds true.