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

fun Talk(): Exploring Kotlin Functions

Nate Ebel
August 16, 2018

fun Talk(): Exploring Kotlin Functions

Nate Ebel

August 16, 2018
Tweet

More Decks by Nate Ebel

Other Decks in Programming

Transcript

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

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

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

    get started - Flexible & Convenient - Enable Freedom to Reimagine @n8ebel #androidsummit 5
  4. 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
  5. Hello Java Method class Greeter { void helloFunctions() { System.out.println("Yay,

    Functions"); } } 4 return type, name, method body @n8ebel #androidsummit 9
  6. 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
  7. Flexible & Convenient 4 Parameter & type flexibility 4 Variations

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

    4 Variable arguments 4 Inferred return types 4 Generic functions @n8ebel #androidsummit 15
  9. Parameter Basics fun helloFunctions(excitingThing:String) { println("Yay, " + excitingThing) }

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

    + excitingThing) } helloFunctions("Yay", "functions") // outputs "Yay, functions" @n8ebel #androidsummit 18
  11. 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
  12. Default Parameters 4 function parameters can have default values 4

    when an argument is omitted, the default value is used @n8ebel #androidsummit 21
  13. 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
  14. Default Parameters & Java 4 Java doesn't have default parameter

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

    GreetingFunctionsKt.helloFunctions("Yay", "functions"); // does not compile GreetingFunctionsKt.helloFunctions("Yay"); } @n8ebel #androidsummit 24
  16. 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
  17. 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
  18. Named Arguments helloFunctions(exclamation = "yay!", excitingThing = "functions") 4 Much

    easier to understand with named arguments @n8ebel #androidsummit 31
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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
  27. 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
  28. Variable Number of Arguments This works helloFunctions("Android Summit", "Kotlin", "Android")

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

    = "yay!", "Kotlin", "Android") // error: "no matching function" @n8ebel #androidsummit 43
  30. 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
  31. 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
  32. 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
  33. 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
  34. 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
  35. 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
  36. Generic Functions 4 Like classes, functions may have generic type

    parameters 4 many stdlib functions are built with generics @n8ebel #androidsummit 52
  37. Variations In Scope 4 Top-level 4 Member functions 4 Local

    4 CompanionObject 4 Extension functions @n8ebel #androidsummit 55
  38. 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
  39. Top-Level Function Patterns 4 Replace stateless classes filled with static

    methods 4 Swap your "Util" or "Helper" classes with functions @n8ebel #androidsummit 58
  40. 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
  41. Top-Level Function Considerations Generated Code public class LoggingKt { public

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

    from Java LoggingKt.log(new Throwable("oops")) @n8ebel #androidsummit 62
  43. 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
  44. Top-Level Function Considerations // call from Kotlin log(Throwable("oops")) // call

    from Java LoggingFunctions.log(new Throwable("oops")) @n8ebel #androidsummit 65
  45. Member Functions 4 Function associated with a class or object

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

    } // create instance of class Speaker and call giveTalk() Speaker().giveTalk() @n8ebel #androidsummit 68
  47. 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
  48. 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
  49. 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
  50. Local Functions fun outerFunction(name:String) { fun innerFunction() { println(name) }

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

    4 Avoids code duplication 4 Avoid deep chains of function calls @n8ebel #androidsummit 74
  52. 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
  53. 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
  54. 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
  55. 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
  56. 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
  57. 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
  58. 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
  59. 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
  60. Companion Objects: Use From Java class AuthFragment : Fragment() {

    ... companion object { fun newInstance(username: String) = ... } } @n8ebel #androidsummit 84
  61. 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
  62. Companion Objects: Renaming class AuthFragment : Fragment() { ... companion

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

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

    // no need to reference `Companion` AuthFragment.newInstance("username") } @n8ebel #androidsummit 89
  65. 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
  66. 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
  67. 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
  68. infix Functions 4 Provides a very clean, human-readable syntax 4

    Core building block of custom DSLs @n8ebel #androidsummit 96
  69. 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
  70. 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
  71. 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
  72. Extension Functions // add a new function to the View

    class fun View.isVisible() = visibility == View.VISIBLE yourView.isVisible() @n8ebel #androidsummit 102
  73. 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
  74. Extension Functions fun Context.showToast( msg: CharSequence, duration: Int = Toast.LENGTH_SHORT)

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

    hood? 4 How are these called from Java? @n8ebel #androidsummit 105
  76. 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
  77. Extension Function Considerations // ContextExtensions.kt fun Context.showToast(...) { ... }

    // when called from Java ContextExtensionsKt.showToast(context, "Toast!"); @n8ebel #androidsummit 108
  78. 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
  79. 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
  80. 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
  81. Higher-Order Functions val predicate = { number:Int -> number >

    5 } listOf(2,4,6,8).filter(predicate) @n8ebel #androidsummit 113
  82. 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
  83. 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
  84. 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
  85. 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
  86. 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
  87. inline Functions // sample usage fun inlineExample(l:Lock) { println("before") synchronized(l)

    { println("action") } println("after") } @n8ebel #androidsummit 121
  88. 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
  89. Fewer Helper Classes 4 ContextHelper, ViewUtils 4 Replace with 4

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

    } catch(error:Throwable) { // handle error } } doTheThingSafely { unsafeFunction() } @n8ebel #androidsummit 125
  91. 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
  92. Android KTX sharedPreferences.edit() .putString("key", "without KTX") .putBoolean("isLessBoilerplate", false) .apply() sharedPreferences.edit

    { putString("key", "with KTX") putBoolean("isLessBoilerplate", true) } @n8ebel #androidsummit 127
  93. 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
  94. 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
  95. Useful Collection Functions 4 orEmpty() 4 arrayOf(), listOf(), mapOf() 4

    associateBy(), groupBy() 4 map(), mapIndexed() 4 reduce(), sum(), filter(), partition() @n8ebel #androidsummit 130
  96. 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
  97. 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
  98. 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