Daniel Lew
January 16, 2018
380

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.

January 16, 2018

Transcript

1. Irrational Exuberance: Kotlin Edition
@danlew42

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

3. https://www.ﬂickr.com/photos/imuttoo/6066349127

4. https://www.ﬂickr.com/photos/peasap/4063408356

5. 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
}

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
public List randomList() {
List list = new ArrayList<>();
if (Math.random() > .5) {
}
return list;
}
fun implicitList(): List {
val list = mutableListOf()
if (Math.random() > .5) {
}
return list
}

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

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

10. 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

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

14. 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

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. Nullability
fun one(input: String) {
if (input.length == 5) {
// Do something
}
}
fun two(input: String?) {
if (input?.length == 5) {
// Do something
}
}

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 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. 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
data class Card(
val id: String,
val name: String,
val description: String?
)

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

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

• Compiler assists null usage

25. Nullability
class MyFragment : Fragment() {
override fun onResume() {
super.onResume()
}
}

26. 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. 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. 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. Nullable Primitives
• Int -> int

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

32. 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

33. 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

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

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

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

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. Companion Objects
• “Where are my static methods?!”

• “Where are my constants?!”

• Singletons associated with a class

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

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

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

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

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

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

49. 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
}
}

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

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

52. 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
}

53. 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
}

54. Standard Functions
• let

• run

• with

• apply

• also

55. 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

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

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

58. Let
class Container {
var message: String? = null
fun messageLength(): Int {
val currMsg = message
if (currMsg != null) {
return currMsg.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 {
message?.let { currMsg ->
return currMsg.length
}
return 0
}
}

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

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

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

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

65. Inline Functions
• Java is optimized for method calls

• Inline only when…

• Avoiding object creation

• Using reiﬁed types

• You can prove there’s a substantial gain from inlining a function

66. Thanks!
• @danlew42

• danlew.net