$30 off During Our Annual Pro Sale. View Details »

Kotlin + Android

Kotlin + Android

Brief overview of Kotlin syntax and features for GDG Lviv Android Spring Barcamp 2016

Dmytro Danylyk

April 21, 2016
Tweet

More Decks by Dmytro Danylyk

Other Decks in Programming

Transcript

  1. Kotlin + Android Dmytro Danylyk Timee GmbH.

  2. Statically typed programming language for the JVM, Android and the

    browser 100% interoperable with Java™
  3. Kotlin Java 6

  4. Android bytecode Java code Kotlin code javac kotlinc Dalvik Art

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

    Plugin
  6. 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" }
  7. 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" }
  8. What does it look like? val a: Int = 1

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

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

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

    val b = 1 var c: Int c = 2
  12. 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
  13. 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
  14. 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 } }
  15. 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
  16. 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
  17. 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
  18. 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
  19. String templates val query = "Kotlin" val language = "en"

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

    val url = "https://www.google.com.ua/#q=$query&language=$language"
  21. 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
  22. Properties class User { var name: String? = null var

    age: Int? = null }
  23. 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}")
  24. Properties class User { var name: String? = null var

    age: Int? = null set(value) { if (value >= 0) field = value } }
  25. Lambdas Function that is not declared, but passed immediately as

    an expression.
  26. Lambdas button.setOnClickListener(object : View.OnClickListener { override fun onClick(v: View?) {

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

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

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

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

  31. Lambdas button.setOnClickListener({ toast("Button clicked") }) Lambda is last parameter, can

    be moved out of the parentheses
  32. Lambdas button.setOnClickListener() { toast("Button clicked") }

  33. Lambdas button.setOnClickListener() { toast("Button clicked") } Lambda is the only

    parameter, parentheses are optional
  34. Lambdas button.setOnClickListener { toast("Button clicked") }

  35. 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?
  36. Data class data class User(val name: String, val age: Int)

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

    val user = User("Dmytro", 24) print(user) > User(name=Dmytro, age=24)
  38. Smart cast fun demo(x: Any) { if (x is String)

    { print(x.length) } else if (x is Int) { print(x * x) } }
  39. 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
  40. 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
  41. Smart cast fun demo(x: Any) { if (x is String)

    { print(x.length) } else if (x is Int) { print(x * x) } }
  42. Null safety Kotlin’s type system is aimed to eliminate NullPointerException’s

    from our code.
  43. Null safety var name: String = null override fun onCreate(savedInstanceState:

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

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

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

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

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

    Bundle?) { super.onCreate(savedInstanceState) name = savedInstanceState?.getString("name") }
  49. 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.
  50. Default values for function parameters fun query(table: String, columns: Array<String>,

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

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

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

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

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

    selection: String? = null, selectionArgs : Array<String>? = null, orderBy: String? = null): Cursor { ... } // function call query("USERS", arrayOf("ID", "NAME"), orderBy = "NAME")
  56. Lazy property Value gets computed only upon first access.

  57. Lazy property val preference = getSharedPreferences("pref") override fun onCreate(savedInstanceState: Bundle?)

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

    { super.onCreate(savedInstanceState) val username = preference.getString("username") } Crash, require context
  59. 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
  60. Lazy property val preference by lazy { getSharedPreferences("pref") } override

    fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val username = preference.getString("username") }
  61. 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?
  62. Extension Functions fun ImageView.loadUrl(url: String) { Picasso.with(context).load(url).into(this) } ViewExtensions.kt imageView.loadUrl("http://....")

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

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

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

    MainActivity.kt
  66. Single-expression functions When a function returns a single expression, the

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

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

    Function returns a single expression
  69. 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
  70. Single-expression functions fun createUrl(search: String): String = "www.google.com.ua/#q=$search" Return type

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

  72. When expression When expression is similar to switch/case in Java,

    but far more powerful.
  73. When expression when (argument) { match1 -> fun1() match2 ->

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

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

    fun2() else -> fun3() } Can be anything
  76. When expression var result: Int = when (argument) { match1

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

    -> fun1() match2 -> fun2() else -> fun3() }
  78. When expression override fun onCreateViewHolder(g: ViewGroup, type: Int) { when

    (type) { TYPE_TITLE -> TitleViewHolder(g) TYPE_DESCRIPTION -> DescriptionViewHolder(g)) } return null }
  79. 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
  80. When expression override fun onCreateViewHolder(g: ViewGroup, type: Int) = when

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

    (type) { TYPE_TITLE -> TitleViewHolder(g) TYPE_DESCRIPTION -> DescriptionViewHolder(g) else -> null }
  82. When expression override fun getItemViewType(index: Int) = when (dataList[index]) {

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

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

    is Title -> TYPE_TITLE is Description -> TYPE_DESCRIPTION else -> TYPE_UNDEFINED }
  85. 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) } }
  86. 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
  87. 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) } }
  88. let, apply, use, with Higher-order functions - function that takes

    functions as parameters, or returns a function.
  89. let (scope function) /** * Calls the specified function [block]

    with `this` value as * its argument and returns its result. */ public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
  90. let Preferences.getUser().let { showUserName(it.name) showUserEmail(it.email) }

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

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

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

    is not null
  94. let Preferences.getUser()?.let { showUserName(it.name) showUserEmail(it.email) }

  95. 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 : Closeable, R> T.use(block: (T) -> R): R { var closed = false try { return block(this) } catch (e: Exception) { // ommitted } finally { if (!closed) { close() } } }
  96. use fun countUsers(): Long { val database = openDatabase() val

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

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

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

  100. with /** * Calls the specified function [block] with the

    given [receiver] * as its receiver and returns its result. */ public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
  101. with recyclerView.setHasFixedSize(true) recyclerView.addItemDecoration(createDecorator()) recyclerView.layoutManager = LinearLayoutManager(applicationContext) recyclerView.adapter = myAdapter

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

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

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

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

    myAdapter }
  106. apply /** * Calls the specified function [block] with `this`

    value as its receiver * and returns `this` value. */ public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
  107. apply fun makeDir(path: String): File { val result: File =

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

  109. Collections val numbers: MutableList<Int> = mutableListOf(1, 2, 3) numbers.add(1) val

    numbers2: List<Int> = listOf(1, 2, 3) numbers2.add(1) No such method
  110. 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]
  111. 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]
  112. Collections mutableListOf(1, null, 2, null, 3, 4, 5, 6, 7,

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

    8, 9) .filterNotNull() .filter { it % 2 == 0 } .sortedDescending() > [8, 6, 4, 2]
  114. Collections getOrElse() find() filter() filterNot() filterNotNull() flatMap() take() takeLast() sortBy()

    sortByDescending() groupBy() map() mapNotNull() all() any() maxBy() minBy() minWith() sumBy() zip() ...
  115. 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?
  116. Kotlin Android Extensions Plugin buildscript { dependencies { classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"

    } } apply plugin: 'kotlin-android-extensions'
  117. 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 {...} } }
  118. 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 {...} } }
  119. 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)
  120. 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 {...} } }
  121. Anko Kotlin library from JetBrains which provide API to make

    Android application development faster and easier. What it does?
  122. Anko dependencies { compile "org.jetbrains.anko:anko-common:$anko_version" compile "org.jetbrains.anko:anko-sqlite:$anko_version" }

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

  124. 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?") }
  125. Anko async() { // long background task uiThread { //

    won't be executed if isFinishing() is true toolbar.title = "Done" } }
  126. Anko database.use { createTable("Customer", ifNotExists = true, "_id" to INTEGER

    + PRIMARY_KEY + UNIQUE, "name" to TEXT, "photo" to BLOB) }
  127. Resources www.kotlinlang.org/docs/reference www.try.kotlinlang.org www.antonioleiva.com/kotlin - documentation - online compiler +

    exercises - kotlin blog (by Antonio Leiva)
  128. @dmytrodanylyk slides.com/dmytrodanylyk www.dmytrodanylyk.com S speakerdeck.com/dmytrodanylyk S