Kotlin – Lunch & Learn – What is Kotlin?



Kotlin – Lunch & Learn – What is Kotlin?

0 0


kotlin-lunch-and-learn

Kotlin Lunch & Learn Presentation

On Github erikthered / kotlin-lunch-and-learn

Kotlin

Lunch & Learn

Created by Erik Nelson

What is Kotlin?

A statically-typed JVM languaged developed by JetBrains

  • Development started in 2010
  • Current release: 1.0 Beta 4 (Dec. 22, 2015)

Design Goals

  • To create a Java-compatible language,
  • That compiles at least as fast as Java,
  • Make it safer than Java, i.e. statically check for common pitfalls such as null pointer dereference,
  • Make it more concise than Java by supporting variable type inference, higher-order functions (closures), extension functions, mixins and first-class delegation, etc;
  • And, keeping the useful level of expressiveness (see above), make it way simpler than the most mature competitor – Scala.

Features

  • Concise syntax
  • Compiles to JVM bytecode or Javascript
  • Interoperates seamlessly with Java
  • Null safety
  • A lot more!

Cool Minor Features

  • Package name is independent of directory structure
  • Elvis operator
  • No checked exceptions

Things not covered in this presentation

(But worth mentioning)

  • Generics
  • Destructuring declarations
  • Type-safe builders
  • Operator overloading

Syntax

package hello

fun main(args: Array) : Unit {
   println("Hello World!")
}
                        

or more simply:

package hello

fun main(args: Array) {
   println("Hello World!")
}
                        

Class Syntax

class Greeter(val name: String) {
   fun greet() {
      println("Hello, $name")
   }
}

fun main(args: Array) {
   Greeter(args[0]).greet()
}
                        

Variables

Immutable variables

val a: Int = 1
val b = 1   // `Int` type is inferred
val c: Int  // Type required when no initializer is provided
c = 1       // definite assignment

Mutable variables

var x = 5 // `Int` type is inferred
x += 1

Lambda Syntax

Abbreviated form:

val sum = { x: Int, y: Int -> x + y }

Full syntactic form:

val sum: (Int, Int) -> Int = { x, y -> x + y }

One param lambda:

ints.filter { it > 0 } // this literal is of type '(it: Int) -> Boolean'

A lambda expression is always surrounded by curly braces, parameter declarations in the full syntactic form go inside parentheses and have optional type annotations, the body goes after an -> sign. If we leave all the optional annotations out, what’s left looks like this:

It’s very common that a lambda expression has only one parameter. If Kotlin can figure the signature out itself, it allows us not to declare the only parameter, and will implicitly declare it for us under the name it:

Note that if a function takes another function as the last parameter, the lambda expression argument can be passed outside the parenthesized argument list.

Declaring a higher-order function

fun  lock(lock: Lock, body: () -> T): T {
  lock.lock()
  try {
    return body()
  }
  finally {
    lock.unlock()
  }
}
                        

Let’s examine the code above: body has a function type: () -> T, so it’s supposed to be a function that takes no parameters and returns a value of type T. It is invoked inside the try-block, while protected by the lock, and its result is returned by the lock() function.

If we want to call lock(), we can pass another function to it as an argument (see function references)

Or we can pass a lambda expression

Control Flow

Most of these structures should be familiar from Java, with some noted improvements

If Expression

In Kotlin if is an expression (it returns a value)

// Traditional usage
var max = a
if (a < b)
  max = b

// With else
var max: Int
if (a > b)
  max = a
else
  max = b

// As expression
val max = if (a > b) a else b

if branches can be blocks. In this case the last expression is the value of the block.

val max = if (a > b) {
    print("Choose a")
    a
  }
  else {
    print("Choose b")
    b
  }

For Loop

for (arg in args) {
    print(arg)
}

While Loop

while (i < args.size){
    print(args[i++])
}

When Expression

The when expression replaces the switch operator of C-style languages

fun cases(obj: Any) {
  when (obj) {
    1          -> print("One")
    "Hello"    -> print("Greeting")
    is Long    -> print("Long")
    !is String -> print("Not a string")
    else       -> print("Unknown")
  }
}

String Templates

val i = 10
val s = "i = $i" // evaluates to "i = 10"

expressions are supported via curly braces:

val s = "abc"
val str = "$s.length is ${s.length}" // "abc.length is 3"

Ranges

Check if a number is in a range:

if (x in 1..y-1)
  print("OK")

Or out of range:

if (x !in 1..y-1)
  print("OUT")

Iterating over a range:

for (x in 1..5)
  print(x)

Collections

Iterating over a collection:

for (name in names)
  println(name)

Check if a collection contains an object

if (text in names) // names.contains(text) is called
  print("Yes")

Filter and map with lambda expressions:

names
    .filter { it.startsWith("A") }
    .sortedBy { it }
    .map { it.toUpperCase() }
    .forEach { print(it) }

Data Classes

Generates the following methods for free:

  • equals()/hashCode()
  • toString()
  • copy()

Example:

data class User(val name: String, val age: Int)

Null Safety

Regular variables can not be null:

var a: String = "abc"
a = null // compilation error

To allow nulls, declare the variable as a nullable type:

var b: String? = "abc"
b = null // ok

Another Example

Property access:

var a: String = "abc"
val l = a.length // ok

Compiler error if you try to access a nullable:

var b: String? = "def"
val l = b.length // error: variable 'b' can be null

How to work with nullable types

Checking for null

val l = if (b != null) b.length else -1

The compiler tracks null checks, which allows for:

if (b != null && b.length > 0)
  print("String of length ${b.length}")
else
  print("Empty string")

* Only works when b is immutable

Safe Calls

Recall this:

var b: String? = "def"
val l = b.length // error: variable 'b' can be null

How do we get around this? The safe operator:

?.

var b: String? = "def"
val l = b?.length

returns b.length if b is not null, otherwise returns null

Chaining Safe Calls

bob?.department?.head?.name
                        

This call will return null if any of the properties in the chain are null

Elvis Operator

val l: Int = if (b != null) b.length else -1
                        

The above can be rewritten more simply:

val l = b?.length ?: -1
                        

Advanced example:

fun foo(node: Node): String? {
  val parent = node.getParent() ?: return null
  val name = node.getName() ?: throw IllegalArgumentException("name expected")
  // ...
}
                        

!! Operator

If your life just feels too empty without NPEs...

val l = b!!.length()
                        

This will return the length if b has value, otherwise will throw a NullPointerException

Extensions

Allow you to extend a class with new functionality without having to inherit from the class or use any type of design pattern such as a Decorator.

Two kinds of extensions:

  • Extension functions
  • Extension properties

No more Util classes!

Extension Functions

To declare an extension function, its name must be prefixed with a receiver type (the type being extended)

fun MutableList.swap(index1: Int, index2: Int) {
  val tmp = this[index1] // 'this' corresponds to the list
  this[index1] = this[index2]
  this[index2] = tmp
}
                        

This can now be used on any MutableList<Int>:

val l = mutableListOf(1, 2, 3)
l.swap(0, 2) // 'this' inside 'swap()' will hold the value of 'l'
                        

Extension Properties

val  List.lastIndex: Int
  get() = size - 1
                        

Extensions don't insert members into classes, so initializers are not allowed. Getters/setters must be used.

Further Reading

Kotlin Lunch & Learn Created by Erik Nelson