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. Irrational Exuberance: Kotlin Edition @danlew42

  2. –Alan Greenspan How do we know when irrational exuberance has

    unduly escalated asset values?
  3. None
  4. https://www.flickr.com/photos/imuttoo/6066349127

  5. https://www.flickr.com/photos/peasap/4063408356

  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 }
  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 }
  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 }
  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 }
  10. Implicit Types fun whoseTypeIsThis() { val foo = listOf("apples", "bananas",

    "pears") .map { it to it.length } .filter { it.second == 3 } .associate { it } .toMutableMap() }
  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
  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
  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
  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] }
  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
  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
  17. Nullability fun one(input: String) { if (input.length == 5) {

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

    // Do something } } fun two(input: String?) { if (input?.length == 5) { // Do something } }
  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
  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
  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
  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
  23. Nullability data class Card( val id: String, val name: String,

    val description: String? )
  24. Nullability data class Card( val id: String, val name: String,

    val description: String? ) • Should I avoid null at all costs?
  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
  26. None
  27. Nullability class MyFragment : Fragment() { override fun onResume() {

    super.onResume() val key = context.getString(R.string.google_api_key) } }
  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)
  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)
  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)
  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)
  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)
  33. Nullable Primitives • Int -> int fun foo() { var

    total = 0 for (value in 0..10) { total += bar(value) ?: 0 } } fun bar(value: Int?) = value
  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
  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
  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 ?: "") } }
  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 ?: "") } }
  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 ?: "") } }
  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 ?: "") } }
  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) } }
  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) } }
  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) } }
  43. Companion Objects • “Where are my static methods?!” • “Where

    are my constants?!” • Common answer: companion objects • Singletons associated with a class
  44. Companion Objects class CompanionObjects { companion object { @JvmStatic fun

    add(one: Int, two: Int) = one + two const val TAG = "Hello" } }
  45. Companion Objects // Accessible anywhere fun functionsCanAlsoBeHere() { } //

    Accessible via SomeObject object SomeObject { @JvmStatic fun functionInOtherObjects() { } }
  46. Companion Objects // Accessible anywhere fun functionsCanAlsoBeHere() { } //

    Accessible via SomeObject object SomeObject { @JvmStatic fun functionInOtherObjects() { } }
  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" }
  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" }
  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" }
  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 } }
  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 } }
  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" } }
  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" } }
  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 }
  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 }
  56. Standard Functions • let • run • with • apply

    • also
  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
  58. Let class Container { var message: String? = null fun

    messageLength(): Int { if (message != null) { return message.length } return 0 } }
  59. Let class Container { var message: String? = null fun

    messageLength(): Int { val currMsg = message if (currMsg != null) { return currMsg.length } return 0 } }
  60. Let class Container { var message: String? = null fun

    messageLength(): Int { val currMsg = message if (currMsg != null) { return currMsg.length } return 0 } }
  61. Let class Container { var message: String? = null fun

    messageLength(): Int { val currMsg = message if (currMsg != null) { return currMsg.length } return 0 } }
  62. Let class Container { var message: String? = null fun

    messageLength(): Int { message?.let { currMsg -> return currMsg.length } return 0 } }
  63. Let class Container { var message: String? = null fun

    messageLength(): Int { message?.let { currMsg -> return currMsg.length } return 0 } }
  64. Let class Container { var message: String? = null fun

    messageLength() = message?.length ?: 0 }
  65. Inline Functions // Original source code fun foo() { bar()

    } inline fun bar() { println("Hello, world!") } // Compiled code fun foo() { println("Hello, world!") }
  66. Inline Functions // Original source code fun foo() { bar()

    } inline fun bar() { println("Hello, world!") } // Compiled code fun foo() { println("Hello, world!") }
  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
  68. Thanks! • @danlew42 • danlew.net