fun Talk(): Exploring Kotlin Functions

Df78ed2e647f52dadaac996721a6bdfb?s=47 Nate Ebel
August 16, 2018

fun Talk(): Exploring Kotlin Functions

Df78ed2e647f52dadaac996721a6bdfb?s=128

Nate Ebel

August 16, 2018
Tweet

Transcript

  1. fun() Talk Exploring Kotlin Functions @n8ebel

  2. ♥ == Kotlin + Functions @n8ebel #androidsummit 2

  3. ♥ == Kotlin + Functions 4 Free functions 4 Higher-order

    functions 4 Rich stdlib @n8ebel #androidsummit 3
  4. ♥ == Kotlin + Functions 4 arrayOf(), listOf() 4 measureTimeMillis

    { doSomething() } 4 orEmpty() 4 forEach(), groupBy() @n8ebel #androidsummit 4
  5. Let's Have Some fun() Functions in Kotlin: - Easy to

    get started - Flexible & Convenient - Enable Freedom to Reimagine @n8ebel #androidsummit 5
  6. Easy to Get Started @n8ebel #androidsummit 6

  7. Easy to Get Started 4 Can use the IDE conversion

    tool 4 Can try online (https://try.kotlinlang.org) 4 Easy to transfer existing knowledge @n8ebel #androidsummit 7
  8. From Java to Kotlin @n8ebel #androidsummit 8

  9. Hello Java Method class Greeter { void helloFunctions() { System.out.println("Yay,

    Functions"); } } 4 return type, name, method body @n8ebel #androidsummit 9
  10. Java Method Converted to Kotlin class Greeter { fun helloFunctions()

    { println("Yay, Functions") } } 4 adds the fun keyword 4 no explicit return type @n8ebel #androidsummit 10
  11. Further Simplification class Greeter { fun helloFunctions() = println("Yay, Functions")

    } @n8ebel #androidsummit 11
  12. Further Simplification fun helloFunctions() = println("Yay, Functions") @n8ebel #androidsummit 12

  13. Flexible & Convenient @n8ebel #androidsummit 13

  14. Flexible & Convenient 4 Parameter & type flexibility 4 Variations

    in scoping 4 Variety of modifiers @n8ebel #androidsummit 14
  15. Parameter & Type Freedom 4 Default parameters 4 Named arguments

    4 Variable arguments 4 Inferred return types 4 Generic functions @n8ebel #androidsummit 15
  16. Parameter Basics @n8ebel #androidsummit 16

  17. Parameter Basics fun helloFunctions(excitingThing:String) { println("Yay, " + excitingThing) }

    helloFunctions("functions") // outputs "Yay, functions" @n8ebel #androidsummit 17
  18. Parameter Basics fun helloFunctions(exclamation:String, excitingThing:String) { println(exclamation + ", "

    + excitingThing) } helloFunctions("Yay", "functions") // outputs "Yay, functions" @n8ebel #androidsummit 18
  19. Default Parameters @n8ebel #androidsummit 19

  20. Default Parameters fun helloFunctions(exclamation:String, excitingThing:String = "functions") { println(exclamation +

    ", " + excitingThing) } // outputs "Yay, functions" helloFunctions("Yay", "functions") // outputs "Yay, functions" helloFunctions("Yay") @n8ebel #androidsummit 20
  21. Default Parameters 4 function parameters can have default values 4

    when an argument is omitted, the default value is used @n8ebel #androidsummit 21
  22. Default Parameters 4 allows us the flexibility of overloads without

    the verbosity of writing them 4 help document the function contract by indicating what "sensible defaults" might be @n8ebel #androidsummit 22
  23. Default Parameters & Java 4 Java doesn't have default parameter

    values 4 must specify all parameter values when calling from Java @n8ebel #androidsummit 23
  24. Default Parameters & Java public class Main { // compiles

    GreetingFunctionsKt.helloFunctions("Yay", "functions"); // does not compile GreetingFunctionsKt.helloFunctions("Yay"); } @n8ebel #androidsummit 24
  25. Default Parameters & Java 4 can use @JvmOverloads to generate

    overloads for each parameter 4 generated overloads will use the specified default values @n8ebel #androidsummit 25
  26. Default Parameters & @JvmOverloads @JvmOverloads fun helloFunctions(exclamation:String, excitingThing:String = "functions")

    { println(exclamation + ", " + excitingThing) } @n8ebel #androidsummit 26
  27. @n8ebel #androidsummit 27

  28. Default Parameters & @JvmOverloads With the generated Java methods now

    available, both Java invocations now compile public class Main { GreetingFunctionsKt.helloFunctions("Yay", "functions"); GreetingFunctionsKt.helloFunctions("Yay"); } @n8ebel #androidsummit 28
  29. Named Arguments @n8ebel #androidsummit 29

  30. Named Arguments helloFunctions("functions", "functions") 4 How do we know which

    value is correct? @n8ebel #androidsummit 30
  31. Named Arguments helloFunctions(exclamation = "yay!", excitingThing = "functions") 4 Much

    easier to understand with named arguments @n8ebel #androidsummit 31
  32. Named Arguments 4 Improve readability of function invocations 4 Modify

    order of passed parameters by using named arguments 4 Refactor parameter count/order without breaking code @n8ebel #androidsummit 32
  33. Named Arguments fun helloFunctions(exclamation:String, excitingThing:String = "functions") { println(exclamation +

    ", " + excitingThing) } helloFunctions("Hooray", "functions") helloFunctions("Hooray") helloFunctions(excitingThing = "functions", exclamation = "Hooray") // all output "Hooray, functions" @n8ebel #androidsummit 33
  34. Named Argument Limitations 4 there are limitations to how named

    & positioned arguments are used 4 once an argument name is specified, all subsequent arguments must be named as well @n8ebel #androidsummit 34
  35. Named Argument Limitations helloFunctions("hooray", "Android Summit") helloFunctions("hooray", excitingThing = "Android

    Summit") // both output "hooray, Android Summit" helloFunctions(excitingThing = "Android Summit", "hooray") // error: Mixing named and positioned arguments not allowed @n8ebel #androidsummit 35
  36. Named Arguments & Java 4 Named arguments are not supported

    from Java 4 Arguments must be passed in the order they are defined @n8ebel #androidsummit 36
  37. Variable Arguments @n8ebel #androidsummit 37

  38. Variable Number of Arguments We can define a parameter to

    accept a variable number of arguments T - use the vararg keyword - vararg param is then treated as an array of type T - default value must now be an array @n8ebel #androidsummit 38
  39. Variable Number of Arguments fun helloFunctions(exclamation:String, vararg excitingThings:String) { for(excitingThing

    in excitingThings) { println(exclamation + ", " + excitingThing) } } helloFunctions("yay!", "Android Summit", "Kotlin", "Android") // outputs: // yay!, Android Summit // yay!, Kotlin // yay!, Android @n8ebel #androidsummit 39
  40. Variable Number of Arguments Typically, a vararg parameter will be

    the last one Can be used in any order if: - other params are called using named argument syntax - last param is a function passed outside the parentheses @n8ebel #androidsummit 40
  41. Variable Number of Arguments This works helloFunctions("yay!", "Android Summit", "Kotlin",

    "Android") helloFunctions("Android Summit", "Kotlin", "Android", exclamation = "yay!") // both output: // yay!, Android Summit // yay!, Kotlin // yay!, Android @n8ebel #androidsummit 41
  42. Variable Number of Arguments This works helloFunctions("Android Summit", "Kotlin", "Android")

    // output: // "Android Summit, Kotlin" // "Android Summit, Android" @n8ebel #androidsummit 42
  43. Variable Number of Arguments This won't compile helloFunctions("Android Summit", exclamation

    = "yay!", "Kotlin", "Android") // error: "no matching function" @n8ebel #androidsummit 43
  44. Variable Number of Arguments Use "spread" operator to pass an

    existing array of values val thingsToBeExcitedAbout = arrayOf("Android Summit", "Kotlin", "Android") helloFunctions("yay!", *thingsToBeExcitedAbout) // output: // yay!, Android Summit // yay!, Kotlin // yay!, Android @n8ebel #androidsummit 44
  45. Variable Number of Arguments helloFunctions("yay!", "coffee", *thingsToBeExcitedAbout) helloFunctions("yay!", *thingsToBeExcitedAbout, "coffee")

    4 "Spreading" can be used alone, or with other passed varargs as well 4 input array to the vararg parameter is handled in order @n8ebel #androidsummit 45
  46. Return Types @n8ebel #androidsummit 46

  47. Return Types What is the return type? fun helloFunctions(exclamation:String, excitingThing:String="functions")

    { println(exclamation + ", " + excitingThing) } 4 If a function does not return any useful value, i ts return type is Unit @n8ebel #androidsummit 47
  48. Return Types These are equivalent fun helloFunctions(exclamation:String, excitingThing:String="functions") : Unit

    { println(exclamation + ", " + excitingThing) } fun helloFunctions(exclamation:String, excitingThing:String="functions") { println(exclamation + ", " + excitingThing) } @n8ebel #androidsummit 48
  49. Return Types: Non-Unit Type Functions with a block body require

    explicit return type & call for non-Unit functions fun helloFunctions(exclamation:String, excitingThing:String="functions") : String { return exclamation + ", " + excitingThing } @n8ebel #androidsummit 49
  50. Return Types: Non-Unit Type Can infer return type for single-expression

    functions fun helloFunctions(exclamation:String, excitingThing:String="functions") = exclamation + ", " + excitingThing @n8ebel #androidsummit 50
  51. Generic Functions @n8ebel #androidsummit 51

  52. Generic Functions 4 Like classes, functions may have generic type

    parameters 4 many stdlib functions are built with generics @n8ebel #androidsummit 52
  53. @n8ebel #androidsummit 53

  54. Variations In Scope @n8ebel #androidsummit 54

  55. Variations In Scope 4 Top-level 4 Member functions 4 Local

    4 CompanionObject 4 Extension functions @n8ebel #androidsummit 55
  56. Top-Level functions @n8ebel #androidsummit 56

  57. Top-Level functions 4 Not associated with a class or object

    4 Defined within a Kotlin file 4 Belong to their declared file's package 4 Import to use within other packages @n8ebel #androidsummit 57
  58. Top-Level Function Patterns 4 Replace stateless classes filled with static

    methods 4 Swap your "Util" or "Helper" classes with functions @n8ebel #androidsummit 58
  59. Top-Level Function Considerations 4 Not truly removing classes 4 Generated

    as a public static method on a class using a special convention 4 <function's file name>Kt.java @n8ebel #androidsummit 59
  60. Top-Level Function Considerations // inside Logging.kt package logging fun log(error:Throwable)

    {...} @n8ebel #androidsummit 60
  61. Top-Level Function Considerations Generated Code public class LoggingKt { public

    static void log(Throwable error) {...} } @n8ebel #androidsummit 61
  62. Top-Level Function Considerations // call from Kotlin log(Throwable("oops")) // call

    from Java LoggingKt.log(new Throwable("oops")) @n8ebel #androidsummit 62
  63. Top-Level Function Considerations Can override the generated class/file name 4

    Add @file:JvmName(<desired class name>) to function's file 4 Must be before the declared package @n8ebel #androidsummit 63
  64. Top-Level Function Considerations // inside Logging.kt @file:JvmName("LoggingFunctions") package logging fun

    log(error:Throwable) {...} @n8ebel #androidsummit 64
  65. Top-Level Function Considerations // call from Kotlin log(Throwable("oops")) // call

    from Java LoggingFunctions.log(new Throwable("oops")) @n8ebel #androidsummit 65
  66. Member Functions @n8ebel #androidsummit 66

  67. Member Functions 4 Function associated with a class or object

    4 Have access to private members of the class or object @n8ebel #androidsummit 67
  68. Member Functions class Speaker() { fun giveTalk() { ... }

    } // create instance of class Speaker and call giveTalk() Speaker().giveTalk() @n8ebel #androidsummit 68
  69. Member Function Considerations 4 Default arguments can't be changed in

    overridden methods 4 If overriding a method, you must omit the default values @n8ebel #androidsummit 69
  70. Local Functions @n8ebel #androidsummit 70

  71. Local Functions Functions inside of functions 4 Create a function

    that is scoped to another function 4 Useful if your function is only ever called from another function @n8ebel #androidsummit 71
  72. Local Functions 4 Declare like any other function, but within

    an existing function 4 Have access to all params and variables of the enclosing function @n8ebel #androidsummit 72
  73. Local Functions fun outerFunction(name:String) { fun innerFunction() { println(name) }

    ... innerFunction() // will print the passed name } @n8ebel #androidsummit 73
  74. Local Functions Why would you want this? 4 Clean code

    4 Avoids code duplication 4 Avoid deep chains of function calls @n8ebel #androidsummit 74
  75. Local Functions fun parseAccount(response:AccountResponse) : Account { ... val hobbies

    = response.getField("hobbies").map{ val key = it.getField("key") Hobby(key) } val favoriteFoods = response.getField("favorite_foods").map{ val key = it.getField("key") Food(key) } } @n8ebel #androidsummit 75
  76. Local Functions fun parseAccount(response:AccountResponse) : Account { fun parseKey(entity:ResponseEntity) =

    entity.getField("key") ... val hobbies = response.getField("hobbies").map{ val key = parseKey(it) Hobby(key) } val favoriteFoods = response.getField("favorite_foods").map{ val key = parseKey(it) Food(key) } } @n8ebel #androidsummit 76
  77. Local Function Considerations 4 Local function or private method? 4

    Is the logic going to be needed outside the current function? 4 Does the logic need to be tested in isolation? 4 Is the enclosing function still readable? @n8ebel #androidsummit 77
  78. Companion Objects @n8ebel #androidsummit 78

  79. Companion Objects 4 No static method/functions in Kotlin 4 Recommended

    to use top-level functions instead 4 What if you need access to private members of an object? @n8ebel #androidsummit 79
  80. Companion Objects 4 Want to create a factory method? 4

    Define a member function on a companion object to gain access to private members/constructors @n8ebel #androidsummit 80
  81. Companion Objects: Use from Kotlin class Course private constructor(val key:String)

    // won't work // can't access the private constructor fun createCourse(key:String) : Course { return Course(key) } @n8ebel #androidsummit 81
  82. Companion Objects: Use from Kotlin class Course private constructor(val key:String)

    { companion object { fun createCourse(key:String) : Course { return Course(key) } } } @n8ebel #androidsummit 82
  83. Companion Objects: Use from Kotlin class Course private constructor(val key:String)

    { companion object { fun createCourse(key:String) : Course { return Course(key) } } } // can then call the factory method Course.createCourse("somekey") @n8ebel #androidsummit 83
  84. Companion Objects: Use From Java class AuthFragment : Fragment() {

    ... companion object { fun newInstance(username: String) = ... } } @n8ebel #androidsummit 84
  85. Companion Objects: Use From Java @Override protected void onCreate(Bundle savedInstanceState)

    { super.onCreate(savedInstanceState); // explicitly reference `Companion` from Java AuthFragment.Companion.newInstance("username") } @n8ebel #androidsummit 85
  86. Companion Objects: Renaming Usage from Java is verbose AuthFragment.Companion.newInstance("username") @n8ebel

    #androidsummit 86
  87. Companion Objects: Renaming class AuthFragment : Fragment() { ... companion

    object Factory { fun newInstance(username: String) = ... } } AuthFragment.Factory.newInstance("username") @n8ebel #androidsummit 87
  88. Companion Objects: @JvmStatic class AuthFragment : Fragment() { ... companion

    object { @JvmStatic fun newInstance(username: String) = ... } } @n8ebel #androidsummit 88
  89. Companion Objects: @JvmStatic @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

    // no need to reference `Companion` AuthFragment.newInstance("username") } @n8ebel #androidsummit 89
  90. Variations @n8ebel #androidsummit 90

  91. Variations 4 infix 4 extension 4 higher-order 4 inline @n8ebel

    #androidsummit 91
  92. infix Functions @n8ebel #androidsummit 92

  93. infix Functions 4 infix keyword enables usage of infix notation

    4 What is infix notation? 4 Can omit the dot & parentheses for a function call 4 "key" to "value" @n8ebel #androidsummit 93
  94. infix Functions 4 Must be a member function or extension

    function 4 Must take a single, non-varargs, parameter with no default value @n8ebel #androidsummit 94
  95. infix Functions class ConferenceAttendee { infix fun addInterest(name:String){...} } //

    call the function without dot or parentheses val attendee = ConferenceAttendee() attendee.addInterest("Kotlin") attendee addInterest "Kotlin" @n8ebel #androidsummit 95
  96. infix Functions 4 Provides a very clean, human-readable syntax 4

    Core building block of custom DSLs @n8ebel #androidsummit 96
  97. infix Functions: stdlib for (i in 0 until 10 step

    2) { println(i) // prints 0,2,4,6,8 } "key" to someValue // creates a Pair var result: Boolean result = true or false // perform logical operations result = true and false @n8ebel #androidsummit 97
  98. infix Functions: Kotlin Test "hello" should haveSubstring("ell") "hello" shouldNot haveSubstring("olleh")

    https://github.com/kotlintest/kotlintest @n8ebel #androidsummit 98
  99. Extension Functions @n8ebel #androidsummit 99

  100. Extension Functions 4 Extend the functionality of an existing class

    4 Defined outside the class 4 Used as if they were a member of a class @n8ebel #androidsummit 100
  101. Why Extension Functions? 4 Clean-up or extend classes & apis

    you don't control 4 Remove helper classes & simplify top-level functions @n8ebel #androidsummit 101
  102. Extension Functions // add a new function to the View

    class fun View.isVisible() = visibility == View.VISIBLE yourView.isVisible() @n8ebel #androidsummit 102
  103. Extension Functions fun showToast( context: Context, msg:String, duration: Int =

    Toast.LENGTH_SHORT) { Toast.makeText(context, msg, duration).show() } showToast(context, "Toast!") @n8ebel #androidsummit 103
  104. Extension Functions fun Context.showToast( msg: CharSequence, duration: Int = Toast.LENGTH_SHORT)

    { Toast.makeText(this, msg, duration).show() } context.showToast("Toast!") @n8ebel #androidsummit 104
  105. Extension Function Considerations 4 How are these generated under the

    hood? 4 How are these called from Java? @n8ebel #androidsummit 105
  106. Extension Function Considerations 4 Generated as static methods that accept

    the receiver object as it's first argument 4 Default behavior is to use <filename>Kt.<functionName> @n8ebel #androidsummit 106
  107. @n8ebel #androidsummit 107

  108. Extension Function Considerations // ContextExtensions.kt fun Context.showToast(...) { ... }

    // when called from Java ContextExtensionsKt.showToast(context, "Toast!"); @n8ebel #androidsummit 108
  109. Higher-Order Functions @n8ebel #androidsummit 109

  110. Higher-Order Functions 4 Functions that take, or return, other functions

    4 Can be lambda or function reference 4 Many examples in the Kotlin standard library apply, also, run @n8ebel #androidsummit 110
  111. Higher-Order Functions 4 Enable interesting patterns & conventions 4 Support

    functional programming 4 Can cleanup setup/teardown patterns such as shared prefs @n8ebel #androidsummit 111
  112. Higher-Order Functions fun getScoreCalculator(level:Level) { return when (level) { Level.EASY

    -> { state:ScoreState -> state.score * 10 } Level.HARD -> { state:ScoreState -> state.score * 5 * state.accuracy } } } @n8ebel #androidsummit 112
  113. Higher-Order Functions val predicate = { number:Int -> number >

    5 } listOf(2,4,6,8).filter(predicate) @n8ebel #androidsummit 113
  114. Higher-Order Functions fun filterTheList(value:Int) = value > 5 listOf(2,4,6,8).filter(::filterTheList) @n8ebel

    #androidsummit 114
  115. Higher-Order Functions If the last parameter of a function is

    a function, you can omit the parentheses listOf(2,4,6,8).filter{ number -> number > 5 } @n8ebel #androidsummit 115
  116. Higher-Order Functions public inline fun <R> synchronized(lock: Any, block: ()

    -> R): R { monitorEnter(lock) try { return block() } finally { monitorExit(lock) } } // call from Kotlin synchronized(database) { database.prePopulate() } @n8ebel #androidsummit 116
  117. Higher-Order Function Performance "Using higher-order functions imposes certain runtime penalties"

    4 Extra class created when using lambda 4 If lambda captures variables, extra object created on each call @n8ebel #androidsummit 117
  118. inline Functions @n8ebel #androidsummit 118

  119. inline Functions 4 Helps solve higher-order function performance hits 4

    Body of the inlined function is substituted for invocations of the function @n8ebel #androidsummit 119
  120. inline Functions inline fun <T> synchronized(lock: Lock, action: () ->

    T): T { lock.lock() try { return action() } finally { lock.unlock() } } // call from Kotlin synchronized(Lock()) {...} @n8ebel #androidsummit 120
  121. inline Functions // sample usage fun inlineExample(l:Lock) { println("before") synchronized(l)

    { println("action") } println("after") } @n8ebel #androidsummit 121
  122. inline Functions With inline the generated code is equivalent to

    this // resulting code fun inlineExample(l:Lock) { println("before") lock.lock() try { println("action") } finally { lock.unlock() } println("after") } @n8ebel #androidsummit 122
  123. Android Reimagined @n8ebel #androidsummit 123

  124. Fewer Helper Classes 4 ContextHelper, ViewUtils 4 Replace with 4

    top-level functions 4 extension functions @n8ebel #androidsummit 124
  125. Less Boilerplate fun doTheThingSafely(theThing:() -> Unit) { try { theThing()

    } catch(error:Throwable) { // handle error } } doTheThingSafely { unsafeFunction() } @n8ebel #androidsummit 125
  126. Upgrade Our Apis Can use extensions, default params, etc to

    cleanup/ simplify/extend common apis 4 Now seeing community supported examples of this 4 Android KTX: https://github.com/android/android- ktx 4 Anko: https://github.com/Kotlin/anko @n8ebel #androidsummit 126
  127. Android KTX sharedPreferences.edit() .putString("key", "without KTX") .putBoolean("isLessBoilerplate", false) .apply() sharedPreferences.edit

    { putString("key", "with KTX") putBoolean("isLessBoilerplate", true) } @n8ebel #androidsummit 127
  128. Android KTX // without KTX supportFragmentManager.beginTransaction() .replace(R.id.fragmentContainer, SomeFragment()) .addToBackStack("name") .commit()

    // with KTX supportFragmentManager.transaction { replace(R.id.fagmentContainer, SomeFragment()) addToBackStack("name") } @n8ebel #androidsummit 128
  129. Cleaner Syntax fun log(msg:String) {...} inline fun runOnBackgroundThread(action:() -> Unit)

    { ... } 4 More fluent syntax 4 Simplify test mocking 4 Avoids extra classes @n8ebel #androidsummit 129
  130. Useful Collection Functions 4 orEmpty() 4 arrayOf(), listOf(), mapOf() 4

    associateBy(), groupBy() 4 map(), mapIndexed() 4 reduce(), sum(), filter(), partition() @n8ebel #androidsummit 130
  131. Useful Collection Functions // easily calculate sum from collection val

    totalDownloadSize = downloadRecords.sumBy { it.downloadSize } // sort all DownloadRecords based on download time val sortedDownloadEntities = downloadRecords.sortedByDescending { record -> record.downloadTime } @n8ebel #androidsummit 131
  132. Useful Collection Functions // return map with all DownloadRecords mapped

    to their parent enrollmentKey val entitiesGroupedByEnrollmentKey = downloadRecords.groupBy { record -> record.enrollmentKey } // ensure we have a non-null collection and map all records to view models val viewModels = inputRecords.orEmpty().map { record -> createViewModel(record) } @n8ebel #androidsummit 132
  133. Useful String Functions 4 isEmpty(), isNotEmpty() 4 substringBefore(), substringAfter() 4

    orEmpty() @n8ebel #androidsummit 133
  134. Useful String Functions if(!url.isNullOrEmpty()) { loadData(url) } viewModel.body = model.description.orEmpty()

    @n8ebel #androidsummit 134
  135. Kotlin functions provide flexibility & freedom in how you build

    your apps @n8ebel #androidsummit 135
  136. Go, and Have fun() 4 Easy to get started with

    functions 4 Flexibility gives convenience 4 Enables us to reimagine how we build our apps @n8ebel #androidsummit 136
  137. Let's Continue the Discussion with("n8ebel").apply { Twitter YouTube .com Medium

    Facebook GitHub } @n8ebel #androidsummit 137
  138. Thanks For Coming @n8ebel #androidsummit 138