Irrational Exuberance: Kotlin Edition

D225ebf0faa666ac7655cc7e4689283c?s=47 Daniel Lew
January 16, 2018

Irrational Exuberance: Kotlin Edition

Talk given at Kotlin Night Twin Cities.

A bit of context, because otherwise this talk makes no sense: my overall point here is that some features of Kotlin are awesome, but can be abused by over-usage. In this talk I listed out some stuff I personally screwed up on by over-using when I first got into Kotlin.

D225ebf0faa666ac7655cc7e4689283c?s=128

Daniel Lew

January 16, 2018
Tweet

Transcript

  1. 3.
  2. 6.

    Implicit Types fun explicitType(): String { val name: String =

    "Dan Lew" val number: Int = 42 return name + number } fun implicitType(): String { val name = "Dan Lew" val number = 42 return name + number }
  3. 7.

    Implicit Types fun explicitType(): String { val name: String =

    "Dan Lew" val number: Int = 42 return name + number } fun implicitType(): String { val name = "Dan Lew" val number = 42 return name + number }
  4. 8.

    Implicit Types public List<String> randomList() { List<String> list = new

    ArrayList<>(); if (Math.random() > .5) { list.add("Hello!"); } return list; } fun implicitList(): List<String> { val list = mutableListOf<String>() if (Math.random() > .5) { list.add("Hello!") } return list }
  5. 9.

    Implicit Types public List<String> randomList() { List<String> list = new

    ArrayList<>(); if (Math.random() > .5) { list.add("Hello!"); } return list; } fun implicitList(): List<String> { val list = mutableListOf<String>() if (Math.random() > .5) { list.add("Hello!") } return list }
  6. 10.

    Implicit Types fun whoseTypeIsThis() { val foo = listOf("apples", "bananas",

    "pears") .map { it to it.length } .filter { it.second == 3 } .associate { it } .toMutableMap() }
  7. 11.

    Expression Functions fun add(one: Int, two: Int): Int { return

    one + two } fun add(one: Int, two: Int): Int = one + two fun add(one: Int, two: Int) = one + two
  8. 12.

    Expression Functions fun add(one: Int, two: Int): Int { return

    one + two } fun add(one: Int, two: Int): Int = one + two fun add(one: Int, two: Int) = one + two
  9. 13.

    Expression Functions fun add(one: Int, two: Int): Int { return

    one + two } fun add(one: Int, two: Int): Int = one + two fun add(one: Int, two: Int) = one + two
  10. 14.

    Expression Functions class Diff(val old: State, val new: State) :

    DiffUtil.Callback() { override fun getOldListSize() = old.itemCount override fun getNewListSize() = new.itemCount override fun areItemsTheSame(oldItemPos: Int, newItemPos: Int) = old.getItemId(oldItemPos) == new.getItemId(newItemPos) override fun areContentsTheSame(oldItemPos: Int, newItemPos: Int) = old.fields[oldItemPos] == new.fields[newItemPos] }
  11. 15.

    Expression Functions fun wayTooMuch() = listOf("apples", "bananas", "pears") .map {

    it to it.length } .filter { it.second == 3 } .associate { it } .toMutableMap() fun alsoTooMuch(input: String?, num: Int) = if (input != null) num * (num + 5) / 3 * input.length else num * num + num / num
  12. 16.

    Expression Functions fun wayTooMuch() = listOf("apples", "bananas", "pears") .map {

    it to it.length } .filter { it.second == 3 } .associate { it } .toMutableMap() fun alsoTooMuch(input: String?, num: Int) = if (input != null) num * (num + 5) / 3 * input.length else num * num + num / num
  13. 17.

    Nullability fun one(input: String) { if (input.length == 5) {

    // Do something } } fun two(input: String?) { if (input?.length == 5) { // Do something } }
  14. 18.

    Nullability fun one(input: String) { if (input.length == 5) {

    // Do something } } fun two(input: String?) { if (input?.length == 5) { // Do something } }
  15. 19.

    Nullability fun length(input: String): Int = input.length fun length(input: String?):

    Int? = input?.length fun length(input: String?): Int { if (input != null) { return input.length } else { return 0 } } fun length(input: String?): Int = input?.length ?: 0
  16. 20.

    Nullability fun length(input: String): Int = input.length fun length(input: String?):

    Int? = input?.length fun length(input: String?): Int { if (input != null) { return input.length } else { return 0 } } fun length(input: String?): Int = input?.length ?: 0
  17. 21.

    Nullability fun length(input: String): Int = input.length fun length(input: String?):

    Int? = input?.length fun length(input: String?): Int { if (input != null) { return input.length } else { return 0 } } fun length(input: String?): Int = input?.length ?: 0
  18. 22.

    Nullability fun length(input: String): Int = input.length fun length(input: String?):

    Int? = input?.length fun length(input: String?): Int { if (input != null) { return input.length } else { return 0 } } fun length(input: String?): Int = input?.length ?: 0
  19. 24.

    Nullability data class Card( val id: String, val name: String,

    val description: String? ) • Should I avoid null at all costs?
  20. 25.

    Nullability data class Card( val id: String, val name: String,

    val description: String? ) • Should I avoid null at all costs? • Compiler assists null usage
  21. 26.
  22. 27.

    Nullability class MyFragment : Fragment() { override fun onResume() {

    super.onResume() val key = context.getString(R.string.google_api_key) } }
  23. 28.

    Nullability val currentContext = context if (currentContext != null) {

    val key = currentContext.getString(R.string.key) } else { throw IllegalStateException("How?!") } val key = context?.getString(R.string.key) ?: throw IllegalStateException("How?!") val key = checkNotNull(context, { "How?!" }).getString(R.string.key) val key = context!!.getString(R.string.key)
  24. 29.

    Nullability val currentContext = context if (currentContext != null) {

    val key = currentContext.getString(R.string.key) } else { throw IllegalStateException("How?!") } val key = context?.getString(R.string.key) ?: throw IllegalStateException("How?!") val key = checkNotNull(context, { "How?!" }).getString(R.string.key) val key = context!!.getString(R.string.key)
  25. 30.

    Nullability val currentContext = context if (currentContext != null) {

    val key = currentContext.getString(R.string.key) } else { throw IllegalStateException("How?!") } val key = context?.getString(R.string.key) ?: throw IllegalStateException("How?!") val key = checkNotNull(context, { "How?!" }).getString(R.string.key) val key = context!!.getString(R.string.key)
  26. 31.

    Nullability val currentContext = context if (currentContext != null) {

    val key = currentContext.getString(R.string.key) } else { throw IllegalStateException("How?!") } val key = context?.getString(R.string.key) ?: throw IllegalStateException("How?!") val key = checkNotNull(context, { "How?!" }).getString(R.string.key) val key = context!!.getString(R.string.key)
  27. 32.

    Nullability val currentContext = context if (currentContext != null) {

    val key = currentContext.getString(R.string.key) } else { throw IllegalStateException("How?!") } val key = context?.getString(R.string.key) ?: throw IllegalStateException("How?!") val key = checkNotNull(context, { "How?!" }).getString(R.string.key) val key = context!!.getString(R.string.key)
  28. 33.

    Nullable Primitives • Int -> int fun foo() { var

    total = 0 for (value in 0..10) { total += bar(value) ?: 0 } } fun bar(value: Int?) = value
  29. 34.

    Nullable Primitives • Int -> int • Int? -> Integer

    fun foo() { var total = 0 for (value in 0..10) { total += bar(value) ?: 0 } } fun bar(value: Int?) = value
  30. 35.

    Nullable Primitives • Int -> int • Int? -> Integer

    fun foo() { var total = 0 for (value in 0..10) { total += bar(value) ?: 0 } } fun bar(value: Int?) = value
  31. 36.

    lateinit class MyActivity : Activity() { var centerView: TextView? =

    null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) centerView = findViewById(R.id.center_view) } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putCharSequence("key", centerView?.text ?: "") } }
  32. 37.

    lateinit class MyActivity : Activity() { var centerView: TextView? =

    null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) centerView = findViewById(R.id.center_view) } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putCharSequence("key", centerView?.text ?: "") } }
  33. 38.

    lateinit class MyActivity : Activity() { var centerView: TextView? =

    null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) centerView = findViewById(R.id.center_view) } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putCharSequence("key", centerView?.text ?: "") } }
  34. 39.

    lateinit class MyActivity : Activity() { var centerView: TextView? =

    null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) centerView = findViewById(R.id.center_view) } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putCharSequence("key", centerView?.text ?: "") } }
  35. 40.

    lateinit class MyActivity : Activity() { lateinit var centerView: TextView

    override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) centerView = findViewById(R.id.center_view) } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putCharSequence("key", centerView.text) } }
  36. 41.

    lateinit class MyActivity : Activity() { lateinit var centerView: TextView

    override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) centerView = findViewById(R.id.center_view) } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putCharSequence("key", centerView.text) } }
  37. 42.

    lateinit class MyActivity : Activity() { lateinit var centerView: TextView

    override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) // centerView = findViewById(R.id.center_view) } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putCharSequence("key", centerView.text) } }
  38. 43.

    Companion Objects • “Where are my static methods?!” • “Where

    are my constants?!” • Common answer: companion objects • Singletons associated with a class
  39. 44.

    Companion Objects class CompanionObjects { companion object { @JvmStatic fun

    add(one: Int, two: Int) = one + two const val TAG = "Hello" } }
  40. 45.

    Companion Objects // Accessible anywhere fun functionsCanAlsoBeHere() { } //

    Accessible via SomeObject object SomeObject { @JvmStatic fun functionInOtherObjects() { } }
  41. 46.

    Companion Objects // Accessible anywhere fun functionsCanAlsoBeHere() { } //

    Accessible via SomeObject object SomeObject { @JvmStatic fun functionInOtherObjects() { } }
  42. 47.

    Lifted Statements fun saySomething(intro: Boolean): String { if (intro) {

    return "Hello" } else { return "Goodbye" } } fun saySomething(intro: Boolean): String { return if (intro) { "Hello" } else { "Goodbye" } } fun saySomething(intro: Boolean): String { return if (intro) "Hello" else "Goodbye" }
  43. 48.

    Lifted Statements fun saySomething(intro: Boolean): String { if (intro) {

    return "Hello" } else { return "Goodbye" } } fun saySomething(intro: Boolean): String { return if (intro) { "Hello" } else { "Goodbye" } } fun saySomething(intro: Boolean): String { return if (intro) "Hello" else "Goodbye" }
  44. 49.

    Lifted Statements fun saySomething(intro: Boolean): String { if (intro) {

    return "Hello" } else { return "Goodbye" } } fun saySomething(intro: Boolean): String { return if (intro) { "Hello" } else { "Goodbye" } } fun saySomething(intro: Boolean): String { return if (intro) "Hello" else "Goodbye" }
  45. 50.

    Lifted Statements fun complexLift(list: List<Int>): Int { if (list.size <

    10) { return list .filter { it > 10 } .maxBy { it }!! } else { val folded = list .drop(10) .filter { it < -10 } .foldRight(1, { i, acc -> i * acc }) val otherThing = list.size % 15 return folded * otherThing } }
  46. 51.

    Lifted Statements fun complexLift(list: List<Int>): Int { return if (list.size

    < 10) { list .filter { it > 10 } .maxBy { it }!! } else { val folded = list .drop(10) .filter { it < -10 } .foldRight(1, { i, acc -> i * acc }) val otherThing = list.size % 15 folded * otherThing } }
  47. 52.

    when enum class Model { CARD, BOARD, LIST, MEMBER }

    fun endpoint(model: Model): String { return when(model) { Model.BOARD -> "/1/board" Model.LIST -> "/1/list" Model.CARD -> "/1/card" Model.MEMBER -> "/1/member" } }
  48. 53.

    when fun size(num: Int): String { return when (num) {

    in 0..99 -> "small" in 100..999 -> "medium" in 1000..9999 -> "large" in 10000..Int.MAX_VALUE -> "gigantic" else -> "negative" } }
  49. 54.

    when fun groupType(model: Model): Int { return when (model) {

    Model.CARD -> 1 else -> 2 } } fun groupType(model: Model): Int { return if (model == Model.CARD) 1 else 2 }
  50. 55.

    when fun groupType(model: Model): Int { return when (model) {

    Model.CARD -> 1 else -> 2 } } fun groupType(model: Model): Int { return if (model == Model.CARD) 1 else 2 }
  51. 57.

    Standard Functions fun <T, R> T.let(block: (T) -> R): R

    fun <R> run(block: () -> R): R fun <T, R> T.run(block: T.() -> R): R fun <T, R> with(receiver: T, block: T.() -> R): R fun <T> T.apply(block: T.() -> Unit): T fun <T> T.also(block: (T) -> Unit): T
  52. 58.

    Let class Container { var message: String? = null fun

    messageLength(): Int { if (message != null) { return message.length } return 0 } }
  53. 59.

    Let class Container { var message: String? = null fun

    messageLength(): Int { val currMsg = message if (currMsg != null) { return currMsg.length } return 0 } }
  54. 60.

    Let class Container { var message: String? = null fun

    messageLength(): Int { val currMsg = message if (currMsg != null) { return currMsg.length } return 0 } }
  55. 61.

    Let class Container { var message: String? = null fun

    messageLength(): Int { val currMsg = message if (currMsg != null) { return currMsg.length } return 0 } }
  56. 62.

    Let class Container { var message: String? = null fun

    messageLength(): Int { message?.let { currMsg -> return currMsg.length } return 0 } }
  57. 63.

    Let class Container { var message: String? = null fun

    messageLength(): Int { message?.let { currMsg -> return currMsg.length } return 0 } }
  58. 64.

    Let class Container { var message: String? = null fun

    messageLength() = message?.length ?: 0 }
  59. 65.

    Inline Functions // Original source code fun foo() { bar()

    } inline fun bar() { println("Hello, world!") } // Compiled code fun foo() { println("Hello, world!") }
  60. 66.

    Inline Functions // Original source code fun foo() { bar()

    } inline fun bar() { println("Hello, world!") } // Compiled code fun foo() { println("Hello, world!") }
  61. 67.

    Inline Functions • Java is optimized for method calls •

    Inline only when… • Avoiding object creation • Using reified types • You can prove there’s a substantial gain from inlining a function