Slide 1

Slide 1 text

Kotlin + Android Dmytro Danylyk Timee GmbH.

Slide 2

Slide 2 text

Statically typed programming language for the JVM, Android and the browser 100% interoperable with Java™

Slide 3

Slide 3 text

Kotlin Java 6

Slide 4

Slide 4 text

Android bytecode Java code Kotlin code javac kotlinc Dalvik Art

Slide 5

Slide 5 text

How to add Kotlin to your project? 1. Add Kotlin Plugin

Slide 6

Slide 6 text

How to add Kotlin to your project? 2. Declare dependencies buildscript { dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } apply plugin: 'kotlin-android' dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" }

Slide 7

Slide 7 text

How to add Kotlin to your project? 2. Declare dependencies buildscript { dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } apply plugin: 'kotlin-android' dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" }

Slide 8

Slide 8 text

What does it look like? val a: Int = 1 Read only variable

Slide 9

Slide 9 text

What does it look like? val a: Int = 1 val b = 1 Int type is inferred

Slide 10

Slide 10 text

What does it look like? val a: Int = 1 val b = 1 var c: Int Mutable variable

Slide 11

Slide 11 text

What does it look like? val a: Int = 1 val b = 1 var c: Int c = 2

Slide 12

Slide 12 text

What does it look like? val a: Int = 1 val b = 1 var c: Int c = 2 val date = Date() print(date.time) Instances of class

Slide 13

Slide 13 text

What does it look like? class Main { fun printSum(a: Int, b: Int) { print(a + b) } fun returnSum(a: Int, b: Int): Int { return a + b } } Class name

Slide 14

Slide 14 text

What does it look like? class Main { fun printSum(a: Int, b: Int) { print(a + b) } fun returnSum(a: Int, b: Int): Int { return a + b } }

Slide 15

Slide 15 text

What does it look like? class Main { fun printSum(a: Int, b: Int) { print(a + b) } fun returnSum(a: Int, b: Int): Int { return a + b } } Function name

Slide 16

Slide 16 text

What does it look like? class Main { fun printSum(a: Int, b: Int) { print(a + b) } fun returnSum(a: Int, b: Int): Int { return a + b } } Function arguments

Slide 17

Slide 17 text

What does it look like? class Main { fun printSum(a: Int, b: Int) { print(a + b) } fun returnSum(a: Int, b: Int): Int { return a + b } } Return type

Slide 18

Slide 18 text

Kotlin Features String templates Properties Lambdas Data class Smart cast Null safety Default values for function parameters Lazy property Extension Functions Single-expression functions When expression let, apply, use, with Collections Kotlin Android Extensions Plugin Anko

Slide 19

Slide 19 text

String templates val query = "Kotlin" val language = "en" val url = "https://www.google.com.ua/#q=$query&language=$language"

Slide 20

Slide 20 text

String templates val query = "Kotlin" val language = "en" val url = "https://www.google.com.ua/#q=$query&language=$language"

Slide 21

Slide 21 text

String templates val query = "Kotlin" val language = "en" val url = "https://www.google.com.ua/#q=$query&language=$language" > https://www.google.com.ua/#q=Kotlin&language=en

Slide 22

Slide 22 text

Properties class User { var name: String? = null var age: Int? = null }

Slide 23

Slide 23 text

Properties class User { var name: String? = null var age: Int? = null } val user = User() user.name = "John" user.age = 24 print("User name:${user.name}")

Slide 24

Slide 24 text

Properties class User { var name: String? = null var age: Int? = null set(value) { if (value >= 0) field = value } }

Slide 25

Slide 25 text

Lambdas Function that is not declared, but passed immediately as an expression.

Slide 26

Slide 26 text

Lambdas button.setOnClickListener(object : View.OnClickListener { override fun onClick(v: View?) { toast("Button clicked") } })

Slide 27

Slide 27 text

Lambdas button.setOnClickListener(object : View.OnClickListener { override fun onClick(v: View?) { toast("Button clicked") } }) An anonymous class

Slide 28

Slide 28 text

Lambdas button.setOnClickListener({ view -> toast("Button clicked") }) A lambda expression

Slide 29

Slide 29 text

Lambdas button.setOnClickListener({ view -> toast("Button clicked") }) Parameter is optional

Slide 30

Slide 30 text

Lambdas button.setOnClickListener({ toast("Button clicked") })

Slide 31

Slide 31 text

Lambdas button.setOnClickListener({ toast("Button clicked") }) Lambda is last parameter, can be moved out of the parentheses

Slide 32

Slide 32 text

Lambdas button.setOnClickListener() { toast("Button clicked") }

Slide 33

Slide 33 text

Lambdas button.setOnClickListener() { toast("Button clicked") } Lambda is the only parameter, parentheses are optional

Slide 34

Slide 34 text

Lambdas button.setOnClickListener { toast("Button clicked") }

Slide 35

Slide 35 text

Data class ● getters, setters ● equals()/hashCode() ● toString() of the form "User(name=John, age=42)" ● copy() function Nothing, just hold data What it does? What it provides?

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

Data class data class User(val name: String, val age: Int) val user = User("Dmytro", 24) print(user) > User(name=Dmytro, age=24)

Slide 38

Slide 38 text

Smart cast fun demo(x: Any) { if (x is String) { print(x.length) } else if (x is Int) { print(x * x) } }

Slide 39

Slide 39 text

Smart cast fun demo(x: Any) { if (x is String) { print(x.length) } else if (x is Int) { print(x * x) } } x is automatically cast to String

Slide 40

Slide 40 text

Smart cast fun demo(x: Any) { if (x is String) { print(x.length) } else if (x is Int) { print(x * x) } } x is automatically cast to Int

Slide 41

Slide 41 text

Smart cast fun demo(x: Any) { if (x is String) { print(x.length) } else if (x is Int) { print(x * x) } }

Slide 42

Slide 42 text

Null safety Kotlin’s type system is aimed to eliminate NullPointerException’s from our code.

Slide 43

Slide 43 text

Null safety var name: String = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) name = savedInstanceState.getString("name") }

Slide 44

Slide 44 text

Null safety var name: String = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) name = savedInstanceState.getString("name") } Compilation error, non null variable

Slide 45

Slide 45 text

Null safety var name: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) name = savedInstanceState.getString("name") }

Slide 46

Slide 46 text

Null safety var name: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) name = savedInstanceState.getString("name") } Compilation error, argument may be null

Slide 47

Slide 47 text

Null safety var name: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) name = savedInstanceState?.getString("name") }

Slide 48

Slide 48 text

Null safety var name: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) name = savedInstanceState?.getString("name") }

Slide 49

Slide 49 text

Default values for function parameters Function parameters can have default values, which are used when a corresponding argument is omitted. This allows for a reduced number of overloads compared to other languages.

Slide 50

Slide 50 text

Default values for function parameters fun query(table: String, columns: Array, selection: String?, selectionArgs : Array?, orderBy: String?): Cursor { ... } // function call query("USERS", arrayOf("ID", "NAME"), null, null, "NAME")

Slide 51

Slide 51 text

Default values for function parameters fun query(table: String, columns: Array, selection: String? = null, selectionArgs : Array? = null, orderBy: String? = null): Cursor { ... } // function call query("USERS", arrayOf("ID", "NAME"), null, null, "NAME") Default value

Slide 52

Slide 52 text

Default values for function parameters fun query(table: String, columns: Array, selection: String? = null, selectionArgs : Array? = null, orderBy: String? = null): Cursor { ... } // function call query("USERS", arrayOf("ID", "NAME"), null, null, "NAME")

Slide 53

Slide 53 text

Default values for function parameters fun query(table: String, columns: Array, selection: String? = null, selectionArgs : Array? = null, orderBy: String? = null): Cursor { ... } // function call query("USERS", arrayOf("ID", "NAME"), null, null, orderBy = "NAME") Named argument

Slide 54

Slide 54 text

Default values for function parameters fun query(table: String, columns: Array, selection: String? = null, selectionArgs : Array? = null, orderBy: String? = null): Cursor { ... } // function call query("USERS", arrayOf("ID", "NAME"), null, null, orderBy = "NAME") Not need

Slide 55

Slide 55 text

Default values for function parameters fun query(table: String, columns: Array, selection: String? = null, selectionArgs : Array? = null, orderBy: String? = null): Cursor { ... } // function call query("USERS", arrayOf("ID", "NAME"), orderBy = "NAME")

Slide 56

Slide 56 text

Lazy property Value gets computed only upon first access.

Slide 57

Slide 57 text

Lazy property val preference = getSharedPreferences("pref") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val username = preference.getString("username") }

Slide 58

Slide 58 text

Lazy property val preference = getSharedPreferences("pref") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val username = preference.getString("username") } Crash, require context

Slide 59

Slide 59 text

Lazy property val preference by lazy { getSharedPreferences("pref") } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val username = preference.getString("username") } Won’t be executed until the property is first used

Slide 60

Slide 60 text

Lazy property val preference by lazy { getSharedPreferences("pref") } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val username = preference.getString("username") }

Slide 61

Slide 61 text

Extension Functions Extensions do not actually modify classes they extend. By defining an extension, you do not insert new members into a class, but merely make new functions callable with the dot-notation on instances of this class. Provides the ability to extend a class with new functionality without having to inherit from the class. What it does? How?

Slide 62

Slide 62 text

Extension Functions fun ImageView.loadUrl(url: String) { Picasso.with(context).load(url).into(this) } ViewExtensions.kt imageView.loadUrl("http://....") MainActivity.kt

Slide 63

Slide 63 text

Extension Functions fun ImageView.loadUrl(url: String) { Picasso.with(context).load(url).into(this) } ViewExtensions.kt imageView.loadUrl("http://....") MainActivity.kt Type being extended

Slide 64

Slide 64 text

Extension Functions fun ImageView.loadUrl(url: String) { Picasso.with(context).load(url).into(this) } ViewExtensions.kt imageView.loadUrl("http://....") MainActivity.kt

Slide 65

Slide 65 text

Extension Functions fun ImageView.loadUrl(url: String) { Picasso.with(context).load(url).into(this) } ViewExtensions.kt imageView.loadUrl("http://....") MainActivity.kt

Slide 66

Slide 66 text

Single-expression functions When a function returns a single expression, the curly braces can be omitted and the body is specified after a = symbol.

Slide 67

Slide 67 text

Single-expression functions fun createUrl(search: String): String { return "www.google.com.ua/#q=$search" }

Slide 68

Slide 68 text

Single-expression functions fun createUrl(search: String): String { return "www.google.com.ua/#q=$search" } Function returns a single expression

Slide 69

Slide 69 text

Single-expression functions fun createUrl(search: String): String = "www.google.com.ua/#q=$search" Curly braces can be omitted body is specified after a = symbol

Slide 70

Slide 70 text

Single-expression functions fun createUrl(search: String): String = "www.google.com.ua/#q=$search" Return type is optional

Slide 71

Slide 71 text

Single-expression functions fun createUrl(search: String) = "www.google.com.ua/#q=$search"

Slide 72

Slide 72 text

When expression When expression is similar to switch/case in Java, but far more powerful.

Slide 73

Slide 73 text

When expression when (argument) { match1 -> fun1() match2 -> fun2() else -> fun3() }

Slide 74

Slide 74 text

When expression when (argument) { match1 -> fun1() match2 -> fun2() else -> fun3() } Can be anything

Slide 75

Slide 75 text

When expression when (argument) { match1 -> fun1() match2 -> fun2() else -> fun3() } Can be anything

Slide 76

Slide 76 text

When expression var result: Int = when (argument) { match1 -> fun1() match2 -> fun2() else -> fun3() } Can return result

Slide 77

Slide 77 text

When expression var result: Int = when (argument) { match1 -> fun1() match2 -> fun2() else -> fun3() }

Slide 78

Slide 78 text

When expression override fun onCreateViewHolder(g: ViewGroup, type: Int) { when (type) { TYPE_TITLE -> TitleViewHolder(g) TYPE_DESCRIPTION -> DescriptionViewHolder(g)) } return null }

Slide 79

Slide 79 text

When expression override fun onCreateViewHolder(g: ViewGroup, type: Int) { when (type) { TYPE_TITLE -> TitleViewHolder(g) TYPE_DESCRIPTION -> DescriptionViewHolder(g)) } return null } Can be placed after a = symbol

Slide 80

Slide 80 text

When expression override fun onCreateViewHolder(g: ViewGroup, type: Int) = when (type) { TYPE_TITLE -> TitleViewHolder(g) TYPE_DESCRIPTION -> DescriptionViewHolder(g)) else -> null }

Slide 81

Slide 81 text

When expression override fun onCreateViewHolder(g: ViewGroup, type: Int) = when (type) { TYPE_TITLE -> TitleViewHolder(g) TYPE_DESCRIPTION -> DescriptionViewHolder(g) else -> null }

Slide 82

Slide 82 text

When expression override fun getItemViewType(index: Int) = when (dataList[index]) { is Title -> TYPE_TITLE is Description -> TYPE_DESCRIPTION else -> TYPE_UNDEFINED }

Slide 83

Slide 83 text

When expression override fun getItemViewType(index: Int) = when (dataList[index]) { is Title -> TYPE_TITLE is Description -> TYPE_DESCRIPTION else -> TYPE_UNDEFINED } Type checks possible

Slide 84

Slide 84 text

When expression override fun getItemViewType(index: Int) = when (dataList[index]) { is Title -> TYPE_TITLE is Description -> TYPE_DESCRIPTION else -> TYPE_UNDEFINED }

Slide 85

Slide 85 text

When expression override fun onBindViewHolder(viewHolder: ViewHolder, index: Int) { val displayable: Displayable = dataList[index] when (displayable) { is Title -> initTitle(viewHolder, displayable) is Description -> initDescription(viewHolder, displayable) } }

Slide 86

Slide 86 text

When expression override fun onBindViewHolder(viewHolder: ViewHolder, index: Int) { val displayable: Displayable = dataList[index] when (displayable) { is Title -> initTitle(viewHolder, displayable) is Description -> initDescription(viewHolder, displayable) } } Smart cast

Slide 87

Slide 87 text

When expression override fun onBindViewHolder(viewHolder: ViewHolder, index: Int) { val displayable: Displayable = dataList[index] when (displayable) { is Title -> initTitle(viewHolder, displayable) is Description -> initDescription(viewHolder, displayable) } }

Slide 88

Slide 88 text

let, apply, use, with Higher-order functions - function that takes functions as parameters, or returns a function.

Slide 89

Slide 89 text

let (scope function) /** * Calls the specified function [block] with `this` value as * its argument and returns its result. */ public inline fun T.let(block: (T) -> R): R = block(this)

Slide 90

Slide 90 text

let Preferences.getUser().let { showUserName(it.name) showUserEmail(it.email) }

Slide 91

Slide 91 text

let Preferences.getUser().let { showUserName(it.name) showUserEmail(it.email) } Refers to user object

Slide 92

Slide 92 text

let Preferences.getUser().let { showUserName(it.name) showUserEmail(it.email) } Variable visibility scope

Slide 93

Slide 93 text

let Preferences.getUser()?.let { showUserName(it.name) showUserEmail(it.email) } Only execute if user is not null

Slide 94

Slide 94 text

let Preferences.getUser()?.let { showUserName(it.name) showUserEmail(it.email) }

Slide 95

Slide 95 text

use (try with resources function) /** * Executes the given [block] function on this resource and then * closes it down correctly whether an exception is thrown or not. */ public inline fun T.use(block: (T) -> R): R { var closed = false try { return block(this) } catch (e: Exception) { // ommitted } finally { if (!closed) { close() } } }

Slide 96

Slide 96 text

use fun countUsers(): Long { val database = openDatabase() val result = database.count("users") database.close() return result }

Slide 97

Slide 97 text

use fun countUsers() = openDatabase().use { it.count("users") } Automatically close database

Slide 98

Slide 98 text

use fun countUsers() = openDatabase().use { it.count("users") } Refers to database object

Slide 99

Slide 99 text

use fun countUsers() = openDatabase().use { it.count("users") }

Slide 100

Slide 100 text

with /** * Calls the specified function [block] with the given [receiver] * as its receiver and returns its result. */ public inline fun with(receiver: T, block: T.() -> R): R = receiver.block()

Slide 101

Slide 101 text

with recyclerView.setHasFixedSize(true) recyclerView.addItemDecoration(createDecorator()) recyclerView.layoutManager = LinearLayoutManager(applicationContext) recyclerView.adapter = myAdapter

Slide 102

Slide 102 text

with with(recyclerView) { recyclerView.setHasFixedSize(true) recyclerView.addItemDecoration(createDecorator()) recyclerView.layoutManager = LinearLayoutManager(applicationContext) recyclerView.adapter = myAdapter }

Slide 103

Slide 103 text

with with(recyclerView) { recyclerView.setHasFixedSize(true) recyclerView.addItemDecoration(createDecorator()) recyclerView.layoutManager = LinearLayoutManager(applicationContext) recyclerView.adapter = myAdapter } Not need

Slide 104

Slide 104 text

with with(recyclerView) { setHasFixedSize(true) addItemDecoration(createDecorator()) layoutManager = LinearLayoutManager(applicationContext) adapter = myAdapter }

Slide 105

Slide 105 text

with with(recyclerView) { setHasFixedSize(true) addItemDecoration(createDecorator()) layoutManager = LinearLayoutManager(applicationContext) adapter = myAdapter }

Slide 106

Slide 106 text

apply /** * Calls the specified function [block] with `this` value as its receiver * and returns `this` value. */ public inline fun T.apply(block: T.() -> Unit): T { block(); return this }

Slide 107

Slide 107 text

apply fun makeDir(path: String): File { val result: File = File(path) result.mkdirs() return result }

Slide 108

Slide 108 text

apply fun makeDir(path: String) = File(path).apply { mkdirs() }

Slide 109

Slide 109 text

Collections val numbers: MutableList = mutableListOf(1, 2, 3) numbers.add(1) val numbers2: List = listOf(1, 2, 3) numbers2.add(1) No such method

Slide 110

Slide 110 text

Collections mutableListOf(1, null, 2, null, 3, 4, 5, 6, 7, 8, 9) .filterNotNull() .filter { it % 2 == 0 } .sortedDescending() > [1, null, 2, null, 3, 4, 5, 6, 7, 8, 9]

Slide 111

Slide 111 text

Collections mutableListOf(1, null, 2, null, 3, 4, 5, 6, 7, 8, 9) .filterNotNull() .filter { it % 2 == 0 } .sortedDescending() > [1, 2, 3, 4, 5, 6, 7, 8, 9]

Slide 112

Slide 112 text

Collections mutableListOf(1, null, 2, null, 3, 4, 5, 6, 7, 8, 9) .filterNotNull() .filter { it % 2 == 0 } .sortedDescending() > [2, 4, 6, 8]

Slide 113

Slide 113 text

Collections mutableListOf(1, null, 2, null, 3, 4, 5, 6, 7, 8, 9) .filterNotNull() .filter { it % 2 == 0 } .sortedDescending() > [8, 6, 4, 2]

Slide 114

Slide 114 text

Collections getOrElse() find() filter() filterNot() filterNotNull() flatMap() take() takeLast() sortBy() sortByDescending() groupBy() map() mapNotNull() all() any() maxBy() minBy() minWith() sumBy() zip() ...

Slide 115

Slide 115 text

Kotlin Android Extensions Plugin Adds a hidden caching function and a field inside each Kotlin Activity. Provides reference to all layout views (which have id’s) with single line of code. What it does? How?

Slide 116

Slide 116 text

Kotlin Android Extensions Plugin buildscript { dependencies { classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" } } apply plugin: 'kotlin-android-extensions'

Slide 117

Slide 117 text

Kotlin Android Extensions Plugin // R.layout.activity_main import kotlinx.android.synthetic.main.activity_main.* public class MyActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) txtTitle.setText("Hello, Kotlin!") btnHello.setOnClickListener {...} } }

Slide 118

Slide 118 text

Kotlin Android Extensions Plugin // R.layout.activity_main import kotlinx.android.synthetic.main.activity_main.* public class MyActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) txtTitle.setText("Hello, Kotlin!") btnHello.setOnClickListener {...} } }

Slide 119

Slide 119 text

Kotlin Android Extensions Plugin // R.layout.activity_main import kotlinx.android.synthetic.main.activity_main.* public class MyActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) txtTitle.setText("Hello, Kotlin!") btnHello.setOnClickListener {...} } } Instead of findView(R.id.txtTitle)

Slide 120

Slide 120 text

Kotlin Android Extensions Plugin // R.layout.activity_main import kotlinx.android.synthetic.main.activity_main.* public class MyActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) txtTitle.setText("Hello, Kotlin!") btnHello.setOnClickListener {...} } }

Slide 121

Slide 121 text

Anko Kotlin library from JetBrains which provide API to make Android application development faster and easier. What it does?

Slide 122

Slide 122 text

Anko dependencies { compile "org.jetbrains.anko:anko-common:$anko_version" compile "org.jetbrains.anko:anko-sqlite:$anko_version" }

Slide 123

Slide 123 text

Anko toast("Hi there!") toast(R.string.message) longToast("Wow, such a duration")

Slide 124

Slide 124 text

Anko alert("Santa", "You were a good boy?") { positiveButton("Yes") { toast("You are now in GOOD list") } negativeButton("No") { toast("You are now in BAD list") } }.show() val countries = listOf("Ukraine", "USA", "UK",) selector("Where are you from?", countries) { i -> toast("So you're living in ${countries[i]}, right?") }

Slide 125

Slide 125 text

Anko async() { // long background task uiThread { // won't be executed if isFinishing() is true toolbar.title = "Done" } }

Slide 126

Slide 126 text

Anko database.use { createTable("Customer", ifNotExists = true, "_id" to INTEGER + PRIMARY_KEY + UNIQUE, "name" to TEXT, "photo" to BLOB) }

Slide 127

Slide 127 text

Resources www.kotlinlang.org/docs/reference www.try.kotlinlang.org www.antonioleiva.com/kotlin - documentation - online compiler + exercises - kotlin blog (by Antonio Leiva)

Slide 128

Slide 128 text

@dmytrodanylyk slides.com/dmytrodanylyk www.dmytrodanylyk.com S speakerdeck.com/dmytrodanylyk S