Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Irrational Exuberance: Kotlin Edition

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.

Daniel Lew

January 16, 2018
Tweet

More Decks by Daniel Lew

Other Decks in Programming

Transcript

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

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

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

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

    val description: String? ) • Should I avoid null at all costs?
  19. Nullability data class Card( val id: String, val name: String,

    val description: String? ) • Should I avoid null at all costs? • Compiler assists null usage
  20. Nullability class MyFragment : Fragment() { override fun onResume() {

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

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

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

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

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

    Accessible via SomeObject object SomeObject { @JvmStatic fun functionInOtherObjects() { } }
  40. 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" }
  41. 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" }
  42. 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. 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 } }
  44. 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 } }
  45. 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" } }
  46. 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" } }
  47. 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 }
  48. 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 }
  49. 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
  50. Let class Container { var message: String? = null fun

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

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

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

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

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

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

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

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

    } inline fun bar() { println("Hello, world!") } // Compiled code fun foo() { println("Hello, world!") }
  59. 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