name: heading layout: true class: center, middle, inverse
name: inverse layout: true class: middle, inverse
template: heading
Chris Dail - @chrisdail
Senior Director, Software Engineering
Slides - github.com/chrisdail/talk-kotlin-intro
template: heading
template: heading
template: heading
PYPL - pypl.github.io
template: heading
template: heading
template: heading
From The Authors:
"We want to become more productive by switching to a more expressive language. At the same time, we cannot accept compromises in terms of either Java interoperability or compilation speed." - JetBrains Blog
- General purpose programming language
- Open Source (Apache 2)
- Statically Typed
- Concise, safe, pragmatic
- Interoperable
- Object Oriented, Procedural, Functional
template: heading
Misconceptions about the language
template: heading
--
Kotlin started in 2011 by JetBrains with 1.0 in 2016
Android support was only announced in 2017
template: heading
--
Swift started in 2010 but Apple only announced it in 2014
template: heading
--
Kotlin is a modern, pragmatic language with excellent interop
Initially meant JVM but now extends to JavaScript and Native
template: heading
--
Java has made some improvements recently but is still far from modern
Don't take my word for it...
template: heading
layout: false
- name
:
type - Variables require initialization
var message: String = "Hello World!"
--
- Inferred Types
var message = "Hello World!"
--
- Immutable is preferable
val message = "Hello World Forever!"
- Interpolation
var planet = "Earth"
println("Hello $planet")
println("HELLO ${planet.toUpperCase()}")
Any
- All classes extend from Any
--
Any?
- Nullable Type
var neverNull: String = "hi"
//neverNull = null // Compilation Error
var message: String? = "hi"
message = null
--
?.
- Null-safe calls
//message.length // Compilation Error
message?.length
// Equivalent Java: (message == null) ? null : message.length
val person = lastName.let {
Person(it)
}
-
Takes the argument as a parameter (default:
it
) -
Result is the last value (functional like a transform/map) --
-
More concise Null-checking
val username = usernameOrNull()
if (username != null) {
}
usernameOrNull()?.let { username ->
}
- Returns left if not null, otherwise right
val length = message?.length ?: -1
- Can be combined with
throw
orreturn
val map = mapOf("name" to "Ivy")
val name = map["name"] ?: throw IllegalArgumentException()
template: heading
- Traditional
var max: Int
if (a > b) {
max = a
} else {
max = b
}
- As Expression
val max = if (a > b) a else b
- Switch-like
when (x) {
-1 -> println("INVALID")
0, 1 -> println("0 or 1")
in 2..100 -> println("x == $x")
else -> println("otherwise")
}
- if-else chain
when {
x.isOdd() -> print("x is odd")
x.isEven() -> print("x is even")
else -> print("x is funny")
}
- No checked exceptions
- Debated topic in the Java community (Spring)
- Leads to empty
try/catch
blocks
try/catch
is an expression (can return a value)
val a: Int = try {
value.toInt()
} catch (e: NumberFormatException) {
-1
}
--
val b = value.toIntOrNull() ?: -1
- Loop over list
for (value in ints) {
println(value)
}
- With Ranges
for (i in 1..3) {
println(i)
}
- Fizz Buzz
for (n in 1..100) {
println(when {
n % 15 == 0 -> "FizzBuzz"
n % 3 == 0 -> "Fizz"
n % 5 == 0 -> "Buzz"
else -> n
})
}
template: heading
- No class required
- Simple function
fun reverse(message: String): String {
return message.reversed()
}
println(reverse("Hello World"))
// dlroW olleH
- One Liner
fun add(a: Int, b: Int) = a + b
println(add(1, 1))
// 2
- Default/Optional Arguments
fun addUser(name: String, city: String = "Moncton", province:
String = "NB", country: String = "Canada") {
// ...
}
addUser("Charlie")
- Named Arguments
addUser("Charlie", province = "NS", city = "Halifax")
- Last line is the returned value
val sum = { x: Int, y: Int ->
x + y
}
- Higher Order Functions
val diff = { x: Int, y: Int -> x - y }
fun eval(x: Int, y: Int, binaryOp: (Int,Int) -> Int): Int {
return binaryOp(x, y)
}
println(eval(2, 1, sum))
// 3
println(eval(2, 1, diff))
// 1
template: heading
- Simple Constructor with init block
class Person(name: String) {
init {
println("Created person with $name")
}
}
- Class with properties
class User(var name: String, var age: Int)
val user = User("Charlie", 20)
println("Created user: ${user.name} age: ${user.age}")
interface Processor {
fun process()
}
open class Base(val p: Int) : Processor {
private val privateField = "cannot see me"
private fun privateFunction() = println(privateField)
override fun process() = println("Processed")
open fun allowsOverride() = println("Can Override")
}
class Derived(p: Int) : Base(p) {
override fun allowsOverride() = println(p)
}
- Basic declaration
class Address {
var name: String = ""
var street: String = ""
var city: String = ""
var state: String? = null
var zip: String = ""
}
- Custom getter/setter
val isEmpty: Boolean
get() = this.size == 0
- Easy way to create DTO / POJO
- Most data classes can be one liners
data class User(var name: String, var age: Int)
- Adds some default functions based on the constructor:
equals()
andhashCode()
pairtoString()
of the formUser(name=Ivy, age=42)
copy()
function - Creates a copy of this class
- Destructuring
val (name, age) = User("Ivy", 1)
- Singleton as object declaration
object UserService {
fun createUser(name: String) {
// ...
}
}
UserService.createUser("charlie")
- Kotlin has no
static
keyword companion object
is an associated object- Fields and functions on the class
class SomeClass() {
val id: Int
init {
id = nextId++
}
companion object {
private var nextId = 1
}
}
- Adding to existing classes
- Extension Functions
fun <T> HttpResponse<T>.successful() = when (statusCode()) {
in 200..299 -> true
else -> false
}
- No 'magic' here. Imported like any other function
template: heading
val empty = emptyList<String>()
val list = listOf(1, 1, 2, 3)
val mutableList = mutableListOf<String>()
mutableList.add("hello")
println(list[0])
// 1
println(list + listOf(5, 8))
// [1, 1, 2, 3, 5, 8]
println(setOf(1, 1, 2, 3))
// [1, 2, 3]
val emptyMap = emptyMap<String,String>()
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
println(map["b"])
// 2
val mutableMap = mutableMapOf<String,String>()
mutableMap["hello"] = "world"
- Great standard library for manipulating
filter
,map
,find
,fold
,forEach
,groupBy
,min/max
,reduce
,sort
- Example:
- Find the minimum
ttl
value between both sources and destinations
- Find the minimum
data class DnsResult(val target: String, val ttl: Long)
val sources = listOf<DnsResult>(/* ... */)
val destinations = listOf<DnsResult>(/* ... */)
--
- Solution:
val minTtl = (sources + destinations).map { it.ttl }.min()
template: heading
- Coroutines
- Inline functions
- Generics, Covariance, Reified
- Type Safe Builders
- Lazy initialization
- Operator overloading
- Java Interop: Platform Types
- Scoping Functions:
let
,run
,apply
,also
- Kotlin.js
template: heading
Example using Open Source project: Floodlight
Conversion tooling (IntelliJ)
Collection<IFloodlightModule> mods = serviceMap.get(s);
if (mods == null) {
mods = new ArrayList<IFloodlightModule>();
serviceMap.put(s, mods);
}
mods.add(m);
int dupInConf = 0;
for (IFloodlightModule cMod : mods) {
if (mList.contains(cMod.getClass().getCanonicalName()))
dupInConf += 1;
}
if (dupInConf > 1) {
StringBuilder sb = new StringBuilder();
for (IFloodlightModule mod : mods) {
sb.append(mod.getClass().getCanonicalName());
sb.append(", ");
}
String duplicateMods = sb.toString();
String mess = "ERROR! The configuration file " +
"specifies more than one module that " +
"provides the service " +
s.getCanonicalName() +
". Please specify only ONE of the " +
"following modules in the config file: " +
duplicateMods;
throw new FloodlightModuleException(mess);
}
Java
Collection<IFloodlightModule> mods = serviceMap.get(s);
if (mods == null) {
mods = new ArrayList<IFloodlightModule>();
serviceMap.put(s, mods);
}
mods.add(m);
Kotlin
val mods = serviceMap.getOrPut(s) { mutableListOf() }
mods.add(m)
Java
int dupInConf = 0;
for (IFloodlightModule cMod : mods) {
if (mList.contains(cMod.getClass().getCanonicalName()))
dupInConf += 1;
}
if (dupInConf > 1) {
//...
}
Kotlin
val intersection = mList.intersect(mods.map {
it.javaClass.canonicalName
})
if (intersection.isNotEmpty()) {
//...
}
Java
StringBuilder sb = new StringBuilder();
for (IFloodlightModule mod : mods) {
sb.append(mod.getClass().getCanonicalName());
sb.append(", ");
}
String duplicateMods = sb.toString();
String mess = "ERROR! The configuration file " +
"specifies more than one module that " +
"provides the service " +
s.getCanonicalName() +
". Please specify only ONE of the " +
"following modules in the config file: " +
duplicateMods;
throw new FloodlightModuleException(mess);
Kotlin
throw FloodlightModuleException("""ERROR! The configuration file
|specifies more than one module that provides the service
|${s.canonicalName}. Please specify only ONE of the following
|modules in the config file: ${mods.joinToString(", ")}"""
.trimMargin())
val mods = serviceMap.getOrPut(s) { mutableListOf() }
mods.add(m)
val intersection = mList.intersect(mods.map {
it.javaClass.canonicalName
})
if (intersection.isNotEmpty()) {
throw FloodlightModuleException("""ERROR! The configuration file
|specifies more than one module that provides the service
|${s.canonicalName}. Please specify only ONE of the following
|modules in the config file: ${mods.joinToString(", ")}"""
.trimMargin())
}