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

    View Slide

  2. –Alan Greenspan
    How do we know when irrational exuberance has unduly escalated
    asset values?

    View Slide

  3. View Slide

  4. https://www.flickr.com/photos/imuttoo/6066349127

    View Slide

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

    View Slide

  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
    }

    View Slide

  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
    }

    View Slide

  8. Implicit Types
    public List randomList() {
    List list = new ArrayList<>();
    if (Math.random() > .5) {
    list.add("Hello!");
    }
    return list;
    }
    fun implicitList(): List {
    val list = mutableListOf()
    if (Math.random() > .5) {
    list.add("Hello!")
    }
    return list
    }

    View Slide

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

    View Slide

  10. Implicit Types
    fun whoseTypeIsThis() {
    val foo = listOf("apples", "bananas", "pears")
    .map { it to it.length }
    .filter { it.second == 3 }
    .associate { it }
    .toMutableMap()
    }

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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]
    }

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  26. View Slide

  27. Nullability
    class MyFragment : Fragment() {
    override fun onResume() {
    super.onResume()
    val key = context.getString(R.string.google_api_key)
    }
    }

    View Slide

  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)

    View Slide

  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)

    View Slide

  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)

    View Slide

  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)

    View Slide

  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)

    View Slide

  33. Nullable Primitives
    • Int -> int

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

    View Slide

  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

    View Slide

  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

    View Slide

  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 ?: "")
    }
    }

    View Slide

  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 ?: "")
    }
    }

    View Slide

  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 ?: "")
    }
    }

    View Slide

  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 ?: "")
    }
    }

    View Slide

  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)
    }
    }

    View Slide

  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)
    }
    }

    View Slide

  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)
    }
    }

    View Slide

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

    • “Where are my constants?!”

    • Common answer: companion objects

    • Singletons associated with a class

    View Slide

  44. Companion Objects
    class CompanionObjects {
    companion object {
    @JvmStatic
    fun add(one: Int, two: Int) = one + two
    const val TAG = "Hello"
    }
    }

    View Slide

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

    View Slide

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

    View Slide

  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"
    }

    View Slide

  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"
    }

    View Slide

  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"
    }

    View Slide

  50. Lifted Statements
    fun complexLift(list: List): 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
    }
    }

    View Slide

  51. Lifted Statements
    fun complexLift(list: List): 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
    }
    }

    View Slide

  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"
    }
    }

    View Slide

  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"
    }
    }

    View Slide

  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
    }

    View Slide

  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
    }

    View Slide

  56. Standard Functions
    • let

    • run

    • with

    • apply

    • also

    View Slide

  57. Standard Functions
    fun T.let(block: (T) -> R): R
    fun run(block: () -> R): R
    fun T.run(block: T.() -> R): R
    fun with(receiver: T, block: T.() -> R): R
    fun T.apply(block: T.() -> Unit): T
    fun T.also(block: (T) -> Unit): T

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  64. Let
    class Container {
    var message: String? = null
    fun messageLength() = message?.length ?: 0
    }

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  68. Thanks!
    • @danlew42

    • danlew.net

    View Slide