Share
Embed
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
Transcript
68 slides
-
–Alan Greenspan How do we know when irrational exuberance has
unduly escalated asset values? -
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 } -
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 } -
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 } -
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 } -
Implicit Types fun whoseTypeIsThis() { val foo = listOf("apples", "bananas",
"pears") .map { it to it.length } .filter { it.second == 3 } .associate { it } .toMutableMap() } -
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 -
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 -
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 -
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] } -
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 -
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 -
Nullability fun one(input: String) { if (input.length == 5) {
// Do something } } fun two(input: String?) { if (input?.length == 5) { // Do something } } -
Nullability fun one(input: String) { if (input.length == 5) {
// Do something } } fun two(input: String?) { if (input?.length == 5) { // Do something } } -
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 -
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 -
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 -
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 -
Nullability data class Card( val id: String, val name: String,
val description: String? ) -
Nullability data class Card( val id: String, val name: String,
val description: String? ) • Should I avoid null at all costs? -
Nullability data class Card( val id: String, val name: String,
val description: String? ) • Should I avoid null at all costs? • Compiler assists null usage -
Nullability class MyFragment : Fragment() { override fun onResume() {
super.onResume() val key = context.getString(R.string.google_api_key) } } -
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) -
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) -
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) -
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) -
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) -
Nullable Primitives • Int -> int fun foo() { var
total = 0 for (value in 0..10) { total += bar(value) ?: 0 } } fun bar(value: Int?) = value -
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 -
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 -
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 ?: "") } } -
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 ?: "") } } -
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 ?: "") } } -
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 ?: "") } } -
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) } } -
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) } } -
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) } } -
Companion Objects • “Where are my static methods?!” • “Where
are my constants?!” • Common answer: companion objects • Singletons associated with a class -
Companion Objects class CompanionObjects { companion object { @JvmStatic fun
add(one: Int, two: Int) = one + two const val TAG = "Hello" } } -
Companion Objects // Accessible anywhere fun functionsCanAlsoBeHere() { } //
Accessible via SomeObject object SomeObject { @JvmStatic fun functionInOtherObjects() { } } -
Companion Objects // Accessible anywhere fun functionsCanAlsoBeHere() { } //
Accessible via SomeObject object SomeObject { @JvmStatic fun functionInOtherObjects() { } } -
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" } -
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" } -
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" } -
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 } } -
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 } } -
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" } } -
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" } } -
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 } -
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 } -
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 -
Let class Container { var message: String? = null fun
messageLength(): Int { if (message != null) { return message.length } return 0 } } -
Let class Container { var message: String? = null fun
messageLength(): Int { val currMsg = message if (currMsg != null) { return currMsg.length } return 0 } } -
Let class Container { var message: String? = null fun
messageLength(): Int { val currMsg = message if (currMsg != null) { return currMsg.length } return 0 } } -
Let class Container { var message: String? = null fun
messageLength(): Int { val currMsg = message if (currMsg != null) { return currMsg.length } return 0 } } -
Let class Container { var message: String? = null fun
messageLength(): Int { message?.let { currMsg -> return currMsg.length } return 0 } } -
Let class Container { var message: String? = null fun
messageLength(): Int { message?.let { currMsg -> return currMsg.length } return 0 } } -
Let class Container { var message: String? = null fun
messageLength() = message?.length ?: 0 } -
Inline Functions // Original source code fun foo() { bar()
} inline fun bar() { println("Hello, world!") } // Compiled code fun foo() { println("Hello, world!") } -
Inline Functions // Original source code fun foo() { bar()
} inline fun bar() { println("Hello, world!") } // Compiled code fun foo() { println("Hello, world!") } -
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