100% found this document useful (1 vote)
118 views

Kotlin For Android App Development 2019

Uploaded by

Hongxing Xu
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
118 views

Kotlin For Android App Development 2019

Uploaded by

Hongxing Xu
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 435

Kotlin for Android

App Development
The Pearson Addison-Wesley
Developer’s Library

Visit informit.com/devlibrary for a complete list of available publications.

T he Developer’s Library series from Pearson Addison-Wesley


provides practicing programmers with unique, high-quality
references and tutorials on the latest programming languages and
technologies they use in their daily work. All books in the Developer’s
Library are written by expert technology practioners who are
exceptionally skilled at organizing and presenting information in a way
that is useful for other programmers.

Developer’s Library titles cover a wide range of topics, from open


source programming languages and technologies, mobile application
developments, and web development to Java programming and more.

b
Make sure to connect with us!
informit.com/socialconnect
Kotlin for Android
App Development
Peter Sommerhoff

Boston • Columbus • New York • San Francisco • Amsterdam • Cape Town


Dubai • London • Madrid • Milan • Munich • Paris • Montreal • Toronto • Delhi • Mexico City
São Paulo • Sydney • Hong Kong • Seoul • Singapore • Taipei • Tokyo
Many of the designations used by manufacturers and sellers to distinguish their products Publisher
are claimed as trademarks. Where those designations appear in this book, and the pub- Mark L. Taub
lisher was aware of a trademark claim, the designations have been printed with initial capital
Acquisitions Editor
letters or in all capitals.
Malobika Chakraborty
The author and publisher have taken care in the preparation of this book, but make no Development Editor
expressed or implied warranty of any kind and assume no responsibility for errors or omis- Sheri Replin
sions. No liability is assumed for incidental or consequential damages in connection with or
arising out of the use of the information or programs contained herein. Managing Editor
Sandra Schroeder
For information about buying this title in bulk quantities, or for special sales opportunities
(which may include electronic versions; custom cover designs; and content particular to your Full-Service Production
Manager
business, training goals, marketing focus, or branding interests), please contact our corpo-
rate sales department at corpsales@pearsoned.com or (800) 382-3419. Julie B. Nahil
Project Manager
For government sales inquiries, please contact governmentsales@pearsoned.com.
Suganya Karuppasamy
For questions about sales outside the U.S., please contact intlcs@pearson.com. Copy Editor
Visit us on the Web: informit.com/aw Stephanie M. Geels
Indexer
Library of Congress Control Number: 2018958190
Ken Johnson
Copyright © 2019 Pearson Education, Inc. Proofreader
Cover image © A.F. Smith/Shutterstock Larry Sulky
Technical Reviewers
All rights reserved. This publication is protected by copyright, and permission must be
Miguel Castiblanco
obtained from the publisher prior to any prohibited reproduction, storage in a retrieval
Amanda Hill
system, or transmission in any form or by any means, electronic, mechanical, photocopying,
Ty Smith
recording, or likewise. For information regarding permissions, request forms and the appro-
priate contacts within the Pearson Education Global Rights & Permissions Department, Cover Designer
please visit www.pearsoned.com/permissions/. Chuti Prasertsith

ISBN-13: 978-0-13-485419-9 Compositor


ISBN-10: 0-13-485419-5 codemantra

ScoutAutomatedPrintCode
Contents

Listings xiii
Foreword xxvii
Preface xxix
Acknowledgments xxxii
About the Author xxxiii

I: Learning Kotlin 1

1 Introducing Kotlin 3
What Is Kotlin? 3
Goals and Language Concepts 4
Why Use Kotlin on Android? 5
Java on Android 5
Kotlin on Android 6
Kotlin versus Java 8 6
Tool Support and Community 7
Business Perspective 7
Who’s Using Kotlin? 8
Summary 9

2 Diving into Kotlin 11


Kotlin REPL 11
Variables and Data Types 12
Variable Declarations 12
Basic Data Types 13
Type Inference 14
Conditional Code 15
If and When as Statements 15
Conditional Expressions 17
Loops and Ranges 19
While Loops 19
For Loops 20
Functions 21
Function Signatures 21
Shorthand for Single-Expression Functions 22
Main Function 22
Default Values and Named Parameters 23
vi Contents

Extension Functions 25
Infix Functions 26
Operator Functions 27
Null Safety 29
Nullable Types 29
Working with Nullables 29
Equality Checks 32
Floating Point Equality 32
Exception Handling 33
Principles of Exception Handling 33
Exception Handling in Kotlin 33
Checked and Unchecked Exceptions 35
Summary 36

3 Functional Programming in Kotlin 37


Purpose of Functional Programming 37
Benefits of Functional Programming 38
Functions 39
Lambda Expressions 40
Using Lambdas in Kotlin 40
Higher-Order Functions 41
Inlining Higher-Order Functions 42
Working with Collections 45
Kotlin Collections API versus Java Collections API 45
Instantiating Collections in Kotlin 46
Accessing and Editing Collections 47
Filtering Collections 47
Mapping Collections 48
Grouping Collections 49
Associating Collections 49
Calculating a Minimum, Maximum, and Sum 49
Sorting Collections 50
Folding Collections 51
Chaining Function Calls 52
Scoping Functions 53
Using let 53
Using apply 54
Using with 55
Using run 56
Contents vii

Using also 58
Using use 59
Combining Higher-Order Functions 59
Lambdas with Receivers 60
Lazy Sequences 61
Lazy Evaluation 62
Using Lazy Sequences 63
Performance of Lazy Sequences 65
Summary 67

4 Object Orientation in Kotlin 69


Classes and Object Instantiation 69
Properties 70
Properties versus Fields 71
Getters and Setters 72
Late-Initialized Properties 72
Delegated Properties 74
Predefined Delegates 75
Delegating to a Map 78
Using Delegated Implementations 79
Methods 80
Extension Methods 81
Nested and Inner Classes 82
Primary and Secondary Constructors 82
Primary Constructors 82
Secondary Constructors 84
Inheritance and Overriding Rules 84
Interfaces 85
Abstract Classes 87
Open Classes 88
Overriding Rules 89
Type Checking and Casting 89
Type Checking 89
Type Casting 90
Smart Casts 90
Visibilities 91
Declarations in Classes or Interfaces 92
Top-Level Declarations 93
viii Contents

Data Classes 94
Using Data Classes 94
Inheritance with Data Classes 96
Enumerations 96
Sealed Classes 98
Objects and Companions 101
Object Declarations as Singletons 103
Companion Objects 103
Generics 105
Generic Classes and Functions 105
Covariance and Contravariance 110
Declaration-Site Variance 113
Use-Site Variance 116
Bounded Type Parameters 118
Star Projections 119
Summary 121

5 Interoperability with Java 123


Using Java Code from Kotlin 123
Calling Getters and Setters 124
Handling Nullability 125
Escaping Clashing Java Identifiers 130
Calling Variable-Argument Methods 130
Using Operators 131
Using SAM Types 131
Further Interoperability Considerations 132
Using Kotlin Code from Java 133
Accessing Properties 133
Exposing Properties as Fields 135
Using File-Level Declarations 136
Calling Extensions 137
Accessing Static Members 138
Generating Method Overloads 141
Using Sealed and Data Classes 142
Visibilities 143
Getting a KClass 143
Handling Signature Clashes 144
Using Inline Functions 145
Contents ix

Exception Handling 145


Using Variant Types 146
The Nothing Type 148
Best Practices for Interop 149
Writing Kotlin-Friendly Java Code 149
Writing Java-Friendly Kotlin Code 149
Summary 150

6 Concurrency in Kotlin 151


Concurrency 151
Challenges 153
State-of-the-Art Solutions 155
Kotlin Coroutines 157
Setup 158
Concepts 158
Suspending Functions 159
Coroutine Builders 163
Generators 183
Actors and Channels 185
Concurrency Styles 192
Coroutines in Practice 193
Under the Hood 197
Summary 200

II: Kotlin on Android 203

7 Android App Development with Kotlin: Kudoo App 205


Setting Up Kotlin for Android 205
Using Kotlin in Android Studio 205
Auto-Generated Gradle Configuration 205
Adapting Your Gradle Configuration 207
Using Annotation Processors 208
Converting Java Code to Kotlin 209
App #1: Kudoo, a To-Do List App 210
Creating the Project 210
Adding the Recycler View 212
Adding a Room Database 221
x Contents

Using a ViewModel 227


Integrating LiveData 230
Adding New To-Do Items 233
Enabling Checking Off To-Do Items 237
Summary 239

8 Android App Development with Kotlin: Nutrilicious 241


Setting Up the Project 242
Adding a RecyclerView to the Home Screen 243
Layout for MainActivity 243
Layout for RecyclerView Items 244
Implementing the Food Model 246
Implementing the RecyclerView Adapter 246
Adding the RecyclerView to MainActivity 248
Fetching Data from the USDA Nutrition API 250
Using Retrofit 251
Performing API Requests 256
Mapping JSON Data to Domain Classes 257
Mapping JSON to DTOs 258
Mapping DTOs to Models 260
Introducing a ViewModel for Search 262
Letting Users Search Foods 265
Implementing a Search Interface 265
Introducing Fragments I: The Search Fragment 268
Introducing Fragments II: The Favorites Fragment 276
Store User’s Favorite Foods in a Room Database 280
Fetching Detailed Nutrition Data from the USDA Food
Reports API 288
Integrating the Details Activity 293
Storing Food Details in the Database 302
Adding RDIs for Actionable Data 307
Improving the User Experience 311
Indicating Empty Search Results 311
Indicate Progress in the Details Activity 312
Summary 314
Contents xi

9 Kotlin DSLs 315


Introducing DSLs 315
What Is a DSL? 315
Benefits and Drawbacks 316
Creating a DSL in Kotlin 318
DSL to Build Complex Objects 318
Immutability through Builders 320
Nesting Deeper 323
Introducing @DslMarker 325
Leveraging Language Features 326
DSL for Android Layouts with Anko 328
Creating Layouts Programmatically 328
Anko Dependencies 329
Creating Layouts with Anko 330
Adding Layout Parameters 330
Migrating Kudoo’s AddTodoActivity to Anko Layouts 332
Adding Custom Views 334
Anko Layouts versus XML Layouts 335
DSL for Gradle Build Scripts 335
Migrating Nutrilicious to Gradle Kotlin DSL 335
Using buildSrc in Gradle 340
Benefits and Drawbacks 342
Summary 343

10 Migrating to Kotlin 345


On Software Migrations 345
Risks and Benefits 345
Leading the Change 346
Getting Buy-In 347
Sharing Knowledge 348
Partial or Full Migration 349
Partial Migration 349
Full Migration 350
Where to Start 351
Test Code 351
Production Code 352
Pet Projects 353
Make a Plan 354
xii Contents

Tool Support 354


Java-to-Kotlin Converter 355
Adjusting Autoconverted Code 355
Summary 356

A Further Resources 359


Official Resources 359
Community 359
Functional Programming 360
Kotlin DSLs 360
Migrating to Kotlin 360
Testing 361

Glossary 363
Index 367
Listings
2.1 Declaring Mutable Variables 12
2.2 Declaring Read-Only Variables 12
2.3 Type Inference 14
2.4 Conditions With if 15
2.5 when for Cascades of Conditions 16
2.6 Types of Conditions for when 16
2.7 Arbitrary Boolean Conditions With when 17
2.8 If Expression 18
2.9 Ternary Conditional Operator with if 18
2.10 Using when as an Expression 18
2.11 while Loop 19
2.12 do-while Loop 19
2.13 for Loops 20
2.14 Ranges in for Loops 20
2.15 Declaring a Function 21
2.16 Calling a Function 22
2.17 Transforming the Function Body into a Single Expression 22
2.18 Shorthand Function Notation Using Equals Sign 22
2.19 Declaring a Main Function 23
2.20 Defining Default Values for Parameters 23
2.21 Using Named Parameters 23
2.22 An Overloaded Function 24
2.23 Creating and Calling Extension Functions 25
2.24 Static Resolving of Extension Functions 25
2.25 Importing Extension Functions 26
2.26 The Infix Function to 26
2.27 Calling an Infix Function 27
2.28 Defining Own Infix Functions 27
2.29 Defining and Calling Operators 27
2.30 Nullable and Non-Nullable Types 29
2.31 Accessing Members of Nullable Variables 30
2.32 Elvis Operator 30
2.33 Unsafe Call Operator—Member Access 31
2.34 Unsafe Call Operator—Passing Arguments 31
2.35 Referential and Structural Equality Checks 32
2.36 Equality of Floating Point Values 32
xiv Listings

2.37 Throwing and Catching Exceptions 33


2.38 Using try-catch as an Expression 34
2.39 Using throw as an Expression 35
2.40 Using Nothing for Non-Returning Functions 35
3.1 Function Signature 39
3.2 Function Type 39
3.3 Full Lambda Expression 40
3.4 Assigning a Lambda Expression 40
3.5 Type Inference for Lambda Arguments 41
3.6 Type Inference for Lambda Variables 41
3.7 Implicit Argument Name it 41
3.8 Function Pointers 41
3.9 Defining a Higher-Order Function 42
3.10 Calling a Higher-Order Function 42
3.11 Lambdas as Last Argument 42
3.12 Call to Higher-Order Function 43
3.13 Inlining Higher-Order Functions 43
3.14 Result of Inlined Function Call 43
3.15 Returning from an Enclosing Function 44
3.16 Returning from an Enclosing Function—Inlined Result 44
3.17 Disallowing Non-Local Returns Using crossinline 45
3.18 Helper Functions for Creating Collections 46
3.19 Indexed Access Operator for Reading 47
3.20 Indexed Access Operator for Writing 47
3.21 Filtering a Collection 47
3.22 Searching a Collection 48
3.23 Applying a Function to Each Element Using map 48
3.24 Signature of the map Function 48
3.25 Projections Using map 48
3.26 Grouping Words by Length 49
3.27 Turning a List into a Map 49
3.28 Minimum, Maximum, and Sum by Some Metric 50
3.29 Sorting Collections 50
3.30 Calculating Sum and Product Using fold 51
3.31 Chaining Functions 52
3.32 Scoping with let 53
3.33 Handling Nullables with let 54
Listings xv

3.34 Using apply 54


3.35 Initializing Objects with apply 55
3.36 Return Value of apply 55
3.37 Using with for Multiple Calls on Same Object 56
3.38 Using with to Minimize Scope 56
3.39 Using run with Nullables 57
3.40 Using run for Immediate Function Application 57
3.41 Using run to Minimize Variable Scope 57
3.42 Using run to Turn Parameter into Receiver 58
3.43 Using also for Validation 58
3.44 Using also for Ancillary Operations in a Function Chain 58
3.45 Handling Closeables with use 59
3.46 Combining Scope Operators 59
3.47 Combining All Higher-Order Functions 60
3.48 Signatures of let and run 60
3.49 Filtering and Mapping a Collection or Sequence 62
3.50 Lazy Sequence from Scratch 63
3.51 Lazy Sequence from Collection 63
3.52 Lazy Sequences with generateSequence 63
3.53 Using take and drop 64
3.54 Using take (and drop) Early 64
3.55 Eager Evaluation on Collections 65
3.56 Output for Eager Evaluation 65
3.57 Lazy Evaluation on Sequences 66
3.58 Output for Lazy Evaluation 66
4.1 Class Declaration and Instantiation 69
4.2 Adding a Property the Hard Way 70
4.3 Using a Primary Constructor 70
4.4 Idiomatic Way to Add a Simple Property 70
4.5 Property Access Calls Getter or Setter 71
4.6 Custom Getter and Setter 72
4.7 Using Late-Initialized Properties 73
4.8 Syntax of Delegated Properties 74
4.9 Implementing a Custom Delegate 74
4.10 Using Lazy Properties 75
4.11 Using Observable Properties 77
4.12 Delegating Properties to a Map 78
xvi Listings

4.13 Delegating to an Implementation Using by 79


4.14 Delegated Implementation 79
4.15 Delegating Mutable Set 80
4.16 Declaring and Calling Methods 80
4.17 Extension Methods 81
4.18 Comparing Nested and Inner Classes 82
4.19 Using a Primary Constructor 83
4.20 Adding Modifiers to a Primary Constructor 83
4.21 Upgrading Primary Constructor Parameters to Properties 83
4.22 Combining Primary and Secondary Constructors 84
4.23 Defining an Interface 85
4.24 Defining an Interface with Default Implementations 86
4.25 Inheriting from Interfaces 86
4.26 Abstract Classes and Overriding 87
4.27 Open Classes and Closed-by-Default Classes 88
4.28 Type Checks Using is 89
4.29 Type Casts for Nullable Types 90
4.30 Type Casts and ClassCastException 90
4.31 Smart Casts 91
4.32 Visibility Modifiers 92
4.33 Setting the Visibility of Constructors, Getters, and Setters 93
4.34 Declaring a Data Class 94
4.35 Generated Members of Data Classes 94
4.36 Declaring a Simple Enum Class 96
4.37 Declaring an Enum Class with Members 97
4.38 Working with Enums 97
4.39 Exhaustive when Expression with Enum 98
4.40 Declaring a Sealed Class with Subtypes 98
4.41 Exhaustive when Expression with Sealed Class 99
4.42 Simple Object Expressions 101
4.43 Object Expressions with Supertype 101
4.44 Object Expressions as Return Type 102
4.45 Object Declaration for a Registry Singleton 103
4.46 Accessing Object Members 103
4.47 A Factory as Companion Object 104
4.48 Companion Object with Factory Interface 104
Listings xvii

4.49 Declaring and Instantiating a Generic Class 106


4.50 Generalizing the BinaryTree Class 107
4.51 Declaring and Using a Generic Function 108
4.52 A Generic Function that Accesses Its Type Parameter 109
4.53 A Generic Function Using Reification 109
4.54 Inlined Code of Generic Function 109
4.55 Covariance of Subclasses in Assignments 111
4.56 Covariance of Return Types 111
4.57 Invariance of Arrays in Kotlin (Type-Safe) 111
4.58 Invariance of Java Collections (Type-Safe) 112
4.59 Variance of Read-Only Collections in Kotlin (Type-Safe) 112
4.60 Invariance of Mutable Collections in Kotlin 112
4.61 Covariant Stack Class 113
4.62 An Invariant Mutable Stack Class 113
4.63 A Contravariant Compare<T> Type 114
4.64 Using Contravariance of Compare<T> 114
4.65 Another Contravariant Type Repair<T> 114
4.66 Use-Site Covariance in Assignments 116
4.67 Use-Site Contravariance in Assignments 117
4.68 Function Without Use-Site Variance 117
4.69 Use-Site Variance Increases Function Flexibility 118
4.70 Bounded Type Parameter 118
4.71 Multi-Bounded Type Parameter 118
4.72 Using Star Projections 119
4.73 Star Projections for Variant Types 120
5.1 Using Java Libraries from Kotlin 123
5.2 Calling Java Getters and Setters 124
5.3 Handling Platform Types in Kotlin 126
5.4 Using Nullability Annotations 129
5.5 Handling Name Clashes 130
5.6 Calling a Variable-Argument Method 130
5.7 Using Java Methods as Operators 131
5.8 Using SAM Types from Kotlin 132
5.9 SAM Conversion in Function Arguments 132
5.10 Calling Getters and Setters 133
5.11 Custom Method Name Using @JvmName 134
5.12 Exposing a Property as a Field using @JvmField 135
xviii Listings

5.13 File-Level Declarations in Kotlin 136


5.14 Using @JvmName on File Level 137
5.15 Calling Top-Level Extensions 137
5.16 Calling Class-Local Extensions 138
5.17 Generating Static Fields on the JVM 139
5.18 Using @JvmStatic to Generate Static Methods 140
5.19 Generating Overloads with @JvmOverloads 141
5.20 Working with Sealed Classes 142
5.21 Working with Data Classes 142
5.22 Getting KClass and Class References 144
5.23 JVM Name Clash 144
5.24 Resolving JVM Name Clashes 144
5.25 Calling Inline Functions (from Kotlin) 145
5.26 Generating Throws Clauses 146
5.27 Difference in Decompiled Java Code 146
5.28 Mapping Declaration-Site Variance to Use Site 147
5.29 Adjusting Wildcard Generation 147
5.30 Using the Nothing Type 148
6.1 Asynchrony with Callbacks 156
6.2 Asynchrony with Futures 156
6.3 Asynchrony with async-await Pattern (C#) 157
6.4 Asynchrony with Coroutines 159
6.5 Declaring a Suspending Function 159
6.6 Function Signatures for Callbacks and Futures 160
6.7 Asynchrony with Coroutines 160
6.8 Naming Asynchronous Functions 161
6.9 Cannot Call Suspending Function from Nonsuspending Function 161
6.10 Suspending Functions and Loops 161
6.11 Suspending Functions and Exception Handling 162
6.12 Suspending Functions and Higher-Order Functions 162
6.13 Using runBlocking 163
6.14 Signature of runBlocking (Simplified) 164
6.15 Idiomatic Way to Use runBlocking 164
6.16 Launching a New Coroutine 165
6.17 Waiting for a Coroutine 166
6.18 Launching 100,000 Coroutines 166
6.19 Cancelling a Coroutine 167
Listings xix

6.20 Noncooperative Coroutine Cannot Be Cancelled 168


6.21 Making Coroutine Cooperative Using isActive 168
6.22 Starting Asynchronous Tasks 169
6.23 Running Asynchronous Functions Synchronously 170
6.24 Idiomatic Asynchrony 170
6.25 Handling Exceptions from Asynchronous Calls 171
6.26 Launching a Coroutine on the UI Thread (Android) 172
6.27 Definition of UI Context on Android 172
6.28 Default Coroutine Dispatcher 173
6.29 Dispatching Onto New Thread 173
6.30 Unconfined Dispatcher 173
6.31 Creating a Child Coroutine 173
6.32 Comparing Dispatchers 174
6.33 Switching Context Inside Coroutine 175
6.34 Timeout for Asynchronous Calls 176
6.35 Handling Timeouts 177
6.36 Child Coroutines and Cancellation 177
6.37 Binding Coroutines to Activity #1: The JobHolder Interface 178
6.38 Binding Coroutines to Activity #2: The Activity 178
6.39 Binding Coroutines to Activity #3: Accessing Job from Views 179
6.40 CoroutineName and CoroutineExceptionHandler 179
6.41 CoroutineContext Combination Operator 180
6.42 Combining Contexts 180
6.43 Accessing Context Elements 180
6.44 Starting Coroutines Lazily 181
6.45 Combining Coroutines and CompletableFuture 182
6.46 Creating a Suspending Sequence (Generator) 183
6.47 Declaration of buildSequence 185
6.48 Declaration of SequenceBuilder with Restricted Suspension 185
6.49 A Simple Actor 186
6.50 Actor that Keeps Reading from its Channel 187
6.51 Actor with Conflated Channel 188
6.52 Actor with Multiple Channels 189
6.53 Multiple Actors on Same Channels 190
6.54 Creating a Producer 190
6.55 Actor with Custom Message Type 191
6.56 Transformation between Concurrency Styles 192
xx Listings

6.57 Logging Current Thread (and Coroutine) 193


6.58 Enabling Coroutine Debugging on Android 194
6.59 Implementation of future Coroutine Builder (Simplified) 194
6.60 Custom Await Function (Simplified) 196
6.61 Wrapping Suspending Function for Java Interop 197
6.62 Suspending Function to be Examined 197
6.63 Step 1: Adding Continuation Parameter 198
6.64 Step 2: Removing suspend Modifier 198
6.65 Step 3: Changing Return Type to Any? 198
6.66 Step 4: State Machine (Simplified) 199
7.1 Project’s build.gradle File 206
7.2 Module’s build.gradle File 207
7.3 Add Separate Kotlin Sources Directory to Gradle (Optional) 208
7.4 Dependencies for Targeting JDK 7 or 8 208
7.5 Dependencies for Kotlin Reflection and Test Libraries 208
7.6 Configuring kapt for Annotation Processing (Example: Dagger 2) 209
7.7 RecyclerView Layout 213
7.8 RecyclerView Item Layout 214
7.9 TodoItem Data Class 215
7.10 RecyclerListAdapter Signature 215
7.11 Custom ViewHolder 216
7.12 Enabling Experimental Kotlin Android Extensions 217
7.13 ViewHolder Without findViewById 217
7.14 RecyclerListAdapter.onCreateViewHolder() 218
7.15 RecyclerListAdapter.onBindViewHolder() 218
7.16 RecyclerListAdapter.getItemCount() 219
7.17 Complete RecyclerListAdapter 219
7.18 Setting Up the RecyclerView with the Adapter 220
7.19 Adjusting onCreate 221
7.20 Gradle Dependencies for Room 221
7.21 Enabling the Kotlin Annotation Processor Plugin 222
7.22 TodoItem as an Entity 222
7.23 TodoItemDao for Database Access 222
7.24 AppDatabase 223
7.25 Populating the AppDatabase with Sample Data 224
7.26 Gradle Dependencies for Kotlin Coroutines 226
7.27 Using the Database from MainActivity 226
Listings xxi

7.28 Gradle Dependencies for ViewModel (and LiveData) 228


7.29 Class Header of TodoViewModel 228
7.30 Complete TodoViewModel 228
7.31 Integrating the TodoViewModel into MainActivity 229
7.32 Extension to Retrieve View Models 230
7.33 Returning LiveData from the DAO 231
7.34 Returning LiveData from the ViewModel 231
7.35 Observing the LiveData from MainActivity 232
7.36 Adding setItems to the RecyclerListAdapter 233
7.37 Layout for AddTodoActivity 233
7.38 Layout for the FloatingActionButton 235
7.39 Setting Up the FloatingActionButton 235
7.40 Adjusting onCreate() 235
7.41 Implementing the AddTodoActivity 236
7.42 Enabling Navigating Up from AddTodoActivity to MainActivity 237
7.43 Assigning Event Handlers in the RecyclerView Adapter 238
7.44 Assigning Event Handlers in the RecyclerView Adapter 238
8.1 Bottom Navigation Menu 242
8.2 String Resources for Bottom Navigation Menu 242
8.3 MainActivity Setup for the Bottom Navigation Menu 243
8.4 MainActivity Layout 244
8.5 Layout for RecyclerView Items 244
8.6 Resources for RecyclerView Item Layout 246
8.7 Food Data Class 246
8.8 Enabling the Experimental Kotlin Android Extensions 247
8.9 RecyclerView Adapter 247
8.10 Setting Up the RecyclerView in MainActivity 248
8.11 Hard-Coding the Sample Data 249
8.12 Calling the Setup Method in onCreate 249
8.13 Gradle Dependencies for Network and API Access 251
8.14 Constants Used for Retrofit 251
8.15 Adding API Keys to Your Gradle Properties 252
8.16 Adding a Build Config Field 252
8.17 Building the Retrofit Object 252
8.18 Building the HTTP Client 253
8.19 Building the HTTP Client 253
8.20 Encapsulating Interceptor Creation 254
xxii Listings

8.21 Creating the Interceptor that Adds the API Key to the Query 254
8.22 Defining the Retrofit Interface to Access the Search API 255
8.23 Building the Search API Object 255
8.24 Enabling Internet Access 256
8.25 Gradle Dependencies for Kotlin Coroutines 256
8.26 Coroutine Dispatcher for Network Calls 256
8.27 Performing an API Request 257
8.28 Gradle Dependencies for Moshi 258
8.29 Enabling the Kotlin Annotation Processor 258
8.30 Search API JSON Format 259
8.31 Wrapper DTOs for the Search API 259
8.32 Food DTO for the Search API 260
8.33 Returning the DTO from Retrofit Calls 260
8.34 Secondary Constructor to Map DTO to Model 261
8.35 Displaying Mapped Data in RecyclerView 261
8.36 Removing the Sample Data 262
8.37 Gradle Dependencies for Architecture Components 262
8.38 Step 1: Implementing the SearchViewModel 263
8.39 Step 2: Implementing the SearchViewModel 263
8.40 Using the SearchViewModel in MainActivity 263
8.41 getViewModel Extension Function 264
8.42 Using the SearchViewModel in MainActivity 264
8.43 Encapsulating the Logic For Requests 265
8.44 Menu Resource for Search Interface 265
8.45 String Resource for Search Menu 266
8.46 Searchable Configuration 266
8.47 Setting Up Search in the Android Manifest 266
8.48 Inflating and Setting Up the Search Menu 267
8.49 Handling Search Intents 267
8.50 Layout for the SearchFragment 268
8.51 Layout for the MainActivity 269
8.52 Overriding Lifecycle Methods for the Search Fragment 269
8.53 getViewModel Extension for Fragments 271
8.54 Accessing UI Elements Safely 271
8.55 Adjusting the RecyclerView Setup 271
8.56 Setting Up the SwipeRefreshLayout 272
8.57 Adding a Property for the Fragment 272
Listings xxiii

8.58 Extension Function to Include Fragments 272


8.59 Including a Fragment into the UI 273
8.60 Extension to Store Fragment in Activity’s State 273
8.61 Restoring an Existing Fragment 274
8.62 Adding a Fragment Tag 274
8.63 Adjusting onCreate 274
8.64 Delegating Searches to the Search Fragment 275
8.65 Handling Swipe Refresh 275
8.66 Layout for the Favorites Fragment 276
8.67 Resources for the Layout 277
8.68 Implementing the Favorites Fragment 277
8.69 Moving Common Logic into MainActivity 278
8.70 Removing Duplicated Code from the Fragments 279
8.71 Implementing the Bottom Navigation Menu 279
8.72 Retaining the Last Search Results 280
8.73 Making Food an Entity 281
8.74 Adding the DAO to Access Favorites 281
8.75 Adding the AppDatabase 282
8.76 The View Model to Access Favorite Foods 283
8.77 Coroutine Dispatcher for Database Operations 283
8.78 Adjusting the Adapter to Handle Star Icon Clicks 284
8.79 Creating the Adapter with Click Listener 284
8.80 Toggling Favorites on Star Icon Click 285
8.81 Extension Function to Create Toasts 286
8.82 Favorites Fragment with View Model 286
8.83 Search Fragment 287
8.84 Adding a New Endpoint to the API Interface 289
8.85 JSON Format for Nutrition Details 289
8.86 DTO Wrappers 290
8.87 Details DTOs 291
8.88 Domain Classes for Food Details 292
8.89 Making a Test Request 293
8.90 Horizontal Divider Layout 294
8.91 Divider Dimensions 294
8.92 Details Activity Layout 294
8.93 String Resources For Headlines 296
8.94 Adding an Item Click Listener to the Adapter 297
xxiv Listings

8.95 Defining the Click Handler for List Items 297


8.96 Identifier for the Intent Extra 298
8.97 View Model for Details 298
8.98 Adding the View Model to the DetailsActivity 299
8.99 Using the View Model in the Details Activity 299
8.100 Displaying the Data 300
8.101 String Resource Indicating No Data 301
8.102 Providing Up Navigation 301
8.103 Defining the Entities 302
8.104 Type Converter for List<Nutrient> 302
8.105 Type Converter for NutrientType 303
8.106 DAO for Food Details 304
8.107 Extending the AppDatabase 304
8.108 Enabling Destructive Database Migration in Development 305
8.109 Details Repository as Single Source of Truth 305
8.110 Using the Repository in the View Model 306
8.111 Classes to Represent RDIs 307
8.112 Storing RDIs Statically 307
8.113 Using the Amount Class in Nutrient 308
8.114 Mapping WeightUnit to String And Vice Versa 309
8.115 Displaying a Nutrient 310
8.116 Calculating the Percentage of RDI 310
8.117 Calculating the Percentage of RDI 310
8.118 Showing a Snackbar to Indicate Empty Search 311
8.119 Extension Function to Show Snackbar 312
8.120 Adding a ProgressBar to the Layout 312
8.121 Showing and Hiding the Progress Bar 313
9.1 User DSL 318
9.2 DSL Entry Point 318
9.3 DSL Entry Point—Improved 319
9.4 User Data Class 319
9.5 Adding an Address to a User 319
9.6 Shortcomings of Current DSL 320
9.7 Immutable Data Classes 321
9.8 User Builder 321
9.9 Address Builder 322
9.10 Adjusted Entry Point to the DSL 322
Listings xxv

9.11 Allowing Multiple Addresses 323


9.12 Syntax of Dedicated Addresses Block 323
9.13 Implementing Dedicated Addresses Block 324
9.14 User DSL Annotation 325
9.15 Preventing Nested Calls to Entry Point (in UserBuilder) 325
9.16 Using Variables in Your DSL 326
9.17 Improving DSL Readability 327
9.18 Improving DSL Performance 327
9.19 Creating a Layout Programmatically the Hard Way 328
9.20 Anko Metadependency 330
9.21 Dependencies for Anko Layouts 330
9.22 Simple Anko Layout 330
9.23 Adding Layout Parameters with Anko 331
9.24 Activity Layout with Anko 332
9.25 Modularized Activity Layout Using an Anko Component 333
9.26 Custom Frame Layout 334
9.27 Integrating a Custom Layout into Anko 334
9.28 settings.gradle (Groovy) 336
9.29 settings.gradle.kts (Kotlin) 336
9.30 buildscript Block (Groovy) 336
9.31 buildscript Block (Kotlin) 337
9.32 allprojects Block (Groovy and Kotlin) 337
9.33 Delete Task (Groovy) 337
9.34 Delete Task (Kotlin) 338
9.35 Applying Plugins (Kotlin) 338
9.36 Android Setup (Kotlin) 339
9.37 Adding Dependencies (Kotlin) 339
9.38 Enabling Experimental Android Extensions (Kotlin) 340
9.39 Gradle Config in buildSrc 341
9.40 buildSrc/build.gradle.kts 342
9.41 Using the Gradle Config 342
10.1 Subtle Difference 352
This page intentionally left blank
Foreword
“The limits of my language mean the limits of my world.” This quote goes back to the Austrian-
British philosopher Ludwig Wittgenstein. With the steadily increasing demand for new software
in all the domains that now go digital, it is important that we software developers are efficient
and effective. One important prerequisite for this is of course the use of a programming language
that best fits the problem.

Java was and still is a dominant language for a variety of domains for over a decade now. It was so
successful, originally, because its programs could be downloaded over the internet and executed
on the customer’s computer in a secure sandbox, and later because of the virtual machine
(VM) that allows the developer to abstract away from the ever-varying nasty technical details.
Furthermore, type safety is at the core of the language and is enforced by the VM. The next
generation of languages currently build on these achievements.

Kotlin is among the most prominent languages of this next generation. The language itself has a
high potential to become, at least in some domains, one of the most important languages of the
next decade. This has a number of reasons, both technical and related to marketing.

Kotlin is able to stand out among the currently existing young programming languages due to
its rapid growth in popularity, especially since becoming an official language for Android app
development backed by Google in May 2017. This gave Kotlin an enormous push and was the
reason the discussion in various computer science curricula started to move from the currently
used Java to the even more elegant Kotlin.

Kotlin already provides a mighty tool set, allowing the development of industrial-scale software
at an enormous pace. In addition, it is based on the Java VM and thus can easily be executed on a
wide range of systems. As a consequence, Kotlin is also binary compatible with Java and thus can
reuse Java libraries of all kinds. This immediate availability of libraries is key for a new language.

But foremost, Kotlin is at the same time a strongly typed language and offers efficient language
constructs for both object-oriented and higher-order functional programming. Having a
strong type system enables developers to prevent a number of tedious errors automatically.
This is because a type system guides us to use APIs correctly from the beginning, including
autocompletion in editors, quick access to documentation, effective code navigation, and
sophisticated code refactoring. However, Kotlin also relaxes this type system in various ways to
allow effective development. For example, it provides a type-safe downcast along an inheritance
hierarchy (if necessary). One of the best integrated concepts is that an object type by definition
does not include “null.” If needed, the “null” value needs to be added explicitly, mimicking an
Optional type.

Kotlin also provides various language concepts for higher-order functions, thus allowing the
switch to a functional programming style for certain parts of the system. The integration
of object-oriented and functional programming styles gives us a powerful and effectively
programmable language—even though, to ensure appropriate use of these styles, it is important
to master both.
xxviii Foreword

In this book, Peter Sommerhoff takes a practical approach to teaching Kotlin by providing
a larger set of code listings that demonstrate the discussed language features and by guiding
readers through the development of two Android apps step by step. This language introduction
covers both Kotlin itself and how to use it on Android. Peter finds a good balance between what
is essential and what can be left to readers for their further journey, and thus this book is an
efficient yet comprehensible source for starting programming with Kotlin.

Enjoy reading.

—Bernhard Rumpe
Aachen, Germany
August 2018
Preface

Who This Book Is For


This book is for everyone who has at least one year of programming experience and is intended
to deliver the basic constructs of a programming language. You do not need any prior experience
with Kotlin or Java. Some basic familiarity with Android is helpful for the practical part of this
book, but it is not required.

This book also assumes you’re familiar with some basic terms surrounding software
development. The glossary can help if you’re unfamiliar with a certain term that is used
without explanation.

In short, you fulfill the prerequisites to follow this book if any of the following are true.

 You have a year of programming experience.


 You’re a Java or Android developer.
 You’re a Kotlin developer who wants to use the language to build Android apps.
 You have attended an introductory programming class at university.

This is by no means an exhaustive list. Most important, you should be eager to learn Kotlin and
then put it into practice by building two Android apps.

How This Book Is Structured


This book first covers Kotlin in detail to teach you all essential language features, and then moves
on to using Kotlin in the context of Android. Although Kotlin is introduced comprehensively,
this is by no means a language reference but rather a practical book focusing on gaining
hands-on experience.

Part I, Learning Kotlin, introduces the Kotlin programming language.

 Chapter 1, Introducing Kotlin, gives an overview of Kotlin and motivation to learn this
language.
 Chapter 2, Diving into Kotlin, then covers the basic language constructs such as control
flow, functions, and exceptions. It also focuses on the underlying principles of Kotlin
and how they are reflected in the language design.
 Chapter 3, Functional Programming in Kotlin, shows how Kotlin incorporates
functional programming concepts, how you can use them, and what their potential
benefits are.
 Chapter 4, Object Orientation in Kotlin, deals with object orientation in Kotlin and
interesting features the language provides to write reusable and concise code. It also
again highlights selected language design decisions.
 Chapter 5, Interoperability with Java, discusses common issues, solutions, and best
practices for interoperability with Java.
xxx Preface

 Chapter 6, Concurrency in Kotlin, covers concurrency in Kotlin and is therefore mostly


about coroutines, Kotlin’s answer to asynchronous and concurrent programming.

Part II, Kotlin on Android, lets you put what you learned in Part I into practice by building two
Android apps with Kotlin.
 In Chapter 7, Android App Development with Kotlin: Kudoo App, you will create a simple
to-do list app purely in Kotlin while following best practices, such as using Android’s
Architecture Components, recycler views, and coroutines.
 Chapter 8, Android App Development with Kotlin: Nutrilicious, then guides you through a
more complex app that offers nutrition data to users. The data is fetched from a third-party
API, mapped to an internal data representation, and then presented to the user. This app
introduces more common tools and best practices for Android such as using Retrofit for
network calls, splitting up your app into fragments, and using a repository as a single source
of truth for data.
 Chapter 9, Kotlin DSLs, covers how to create simple domain-specific languages purely in
Kotlin by combining its language features in a clever way. These DSLs can improve code
readability and reliability.
 Chapter 10, Migrating to Kotlin, provides guidance for migrating from Java to Kotlin,
including how to evaluate whether Kotlin is a good fit, common obstacles, and practices
that have helped other companies adopt Kotlin successfully.

How to Follow This Book


Unless you already have a strong understanding of Kotlin, you should definitely go through
Part I before attempting to build the apps in Part II. You may skip Chapter 5, which covers
interoperability with Java, but it is useful to understand what happens under the hood and how
Kotlin compiles to Java bytecode. Also, even if you already know Kotlin, you may want to read
Chapter 6 if you’re not familiar with coroutines yet.

In Part II, you may skip Chapter 9 covering domain-specific languages (DSLs) because it is not a
prerequisite for another chapter. However, DSLs are a popular feature of Kotlin that can improve
your code, and it’s a good way to recap interesting language features from Part I. Finally, if you’re
not interested in migrating an existing app to Kotlin or adopting Kotlin at your company, you
may skip Chapter 10.

For all resources, updates, and news related to this book, please visit its companion website at

https://kotlinandroidbook.com

The site hosts runnable and editable versions of all listings in this book for you to work with, lists
all related resources and GitHub repositories, and presents any updates and corrections for the
book. In short, it’s your companion while reading this book and applying it to your work. The
main GitHub repository for this book can also be found directly at github.com/petersommerhoff/
kotlin-for-android-app-development.
Preface xxxi

After publication, the online code was updated for Kotlin 1.3. Updated code is available here:

Code listings: https://github.com/petersommerhoff/kotlin-for-android-app-development

First app (Kudoo, Chapter 7): https://github.com/petersommerhoff/kudoo-app

Second app (Nutrilicious, Chapter 8): https://github.com/petersommerhoff/


nutrilicious-app

Book Conventions
This book follows several conventions that are used consistently throughout the book.
 Unless stated otherwise, all listings in this book assume they are run as a Kotlin script
file (with .kts extension) and therefore don’t use a main function. Chapters 7 and 8 are
excluded from this because they assume your code runs on Android.
 In Part I, notes marked as “Java Note” are intended for Java developers and typically
compare a concept to its Java counterpart. You can safely ignore them if you don’t
know Java.
 I aimed to use all terms in this book accurately and consistently. If you don’t know a term,
it may be covered in the Glossary.

Register your copy of Kotlin for Android App Development on the InformIT site for convenient
access to updates and/or corrections as they become available. To start the registration
process, go to informit.com/register and log in or create an account. Enter the product
ISBN (9780134854199) and click Submit. Look on the Registered Products tab for an Access
Bonus Content link next to this product, and follow that link to access any available bonus
materials. If you would like to be notified of exclusive offers on new editions and updates,
please check the box to receive email from us.
Acknowledgments
I was very fortunate to have a diverse audience of reviewers for this book who were able to provide
feedback from different perspectives—from students to professors, and from programming
beginners to senior developers and technical leads.

First and foremost, I want to thank Professor Bernhard Rumpe, who holds the Chair for Software
Engineering at RWTH Aachen University, for his continuous support during the creation of this
work, which was created in cooperation with his chair. I also want to thank Katrin Hölldobler,
Research Assistant at the Chair for Software Engineering, for her valuable feedback on several
chapters of the manuscript.

Since the practical part of this book covers Android app development, I also had the great
pleasure to work with three Android developers who reviewed this book. Special thanks go to
Miguel Castiblanco, Software Architect at Globant LLC, and to Ty Smith, Tech Lead Manager
at Uber, for their detailed notes that helped me improve not only the hands-on Android
development part but also the rest of the book. I also thank Amanda Hill, Senior Android
Engineer at PAX Labs, Inc., for her feedback on several chapters of this book that helped me
further improve the draft.

Most of this book covers the Kotlin programming language itself, and I was fortunate to have
two JetBrains employees take a look at parts of this book, too. First, I want to thank Hadi Hariri,
Developer Advocate at JetBrains, for reviewing an early version of this book’s outline. Second,
thanks go to Roman Elizarov, Kotlin Libraries Team Lead at JetBrains, for looking over Chapter 6,
which covers concurrency and Kotlin’s coroutines.

Last but not least, I would like to thank my love, Yifan, for her continuous support and
her detailed feedback on Part I, which helped me greatly improve its readability and
understandability.
About the Author
Peter Sommerhoff is a software developer with a passion for teaching. He’s an online teacher
and the founder of CodeAlong.TV (https://www.codealong.tv/), where he teaches software
development and design to students from around the globe. Kotlin was his first topic to teach
when he started out in 2016. Now, he’s teaching various topics in online courses and live
trainings to an audience of over 35,000 motivated learners.

Peter holds a master’s degree in computer science from RWTH Aachen University in Germany.
Apart from learning and teaching, he enjoys biking tours and playing badminton, as well as
cooking for friends and family.

You can follow his activity primarily on Twitter (https://twitter.com/petersommerhoff) and on


YouTube (https://www.youtube.com/c/PeterSommerhoff), where he shares educational content to
help you become a better software developer.
This page intentionally left blank
I
Learning Kotlin
This page intentionally left blank
1
Introducing Kotlin

The secret to getting ahead is getting started.


Mark Twain

This chapter provides an overview of the Kotlin programming language, the principles on which
it is built, and the benefits it brings compared to Java, with particular focus on writing Android
apps. We will also introduce the ecosystem and community around Kotlin and online resources
for the language.

What Is Kotlin?
Kotlin is a statically typed programming language that is completely open source and free to use.
Kotlin code can compile down to Java bytecode and thus can run on the Java virtual machine
(JVM) and on Android.1 Apart from this, it can be compiled down to JavaScript2 and can even run
on embedded devices and iOS3 devices. The big picture is to be able to target all these platforms
with a single language and share parts of your code base between them. In this book, we focus
only on Kotlin targeting Java bytecode, particularly on Android.

Kotlin was developed by JetBrains,4 the Czech software tool company that develops the IntelliJ
integrated development environment (IDE), among others. Kotlin was designed to be completely
interoperable with Java, meaning that Kotlin code can use any Java classes and libraries and vice
versa. Even though Kotlin is very similar to Java in many regards, its language design is much
cleaner and uses the expertise that was gained in software development and programming language
design since the release of the first version of Java in 1996. For instance, checked exceptions have

1. https://developer.android.com/
2. https://developer.mozilla.org/en-US/docs/Web/javascript/
3. https://developer.apple.com/ios/
4. https://www.jetbrains.com/
4 Chapter 1 Introducing Kotlin

shown drawbacks for large-scale software (so Kotlin only has unchecked exceptions), Java tends to
require much boilerplate code (so Kotlin avoids this), and inheritance comes at the cost of very tight
coupling (so Kotlin classes are closed for inheritance by default). In short, Kotlin does not carry
much legacy, which allows for a clean language that incorporates best practices from the start.

Goals and Language Concepts


The main goal of Kotlin is to provide a pragmatic, concise, interoperable, and safe programming
language.
 Pragmatic means that Kotlin is built to develop real-world enterprise software. The
language design aims to address common issues of large-scale software development and
continuously incorporates industry feedback.
 Concise code is supported by several concepts including type inference, data classes, and
lambda expressions. Conciseness can greatly improve readability and thus maintainability
of a code base, which is essential because code is read much more frequently than it is written.
 Interoperability with Java is indispensable for Kotlin in order to be usable wherever Java
is used today. This includes Android, server-side code, backend development, and desktop
applications. Kotlin itself also makes heavy use of this by reusing and extending the Java
standard library, such as its Collection application programming interface (API).5 Similarly,
it interoperates with JavaScript when targeting that.
 Safe refers to the fact that Kotlin inherently prevents many software errors by its language
design. This is achieved by enforcing several best practices, such as explicitly designing
nonfinal classes for inheritance, and most prominently by providing null safety. In Kotlin,
each type is either explicitly nullable or can never be null, which greatly helps prevent
NullPointerExceptions. When interoperating with Java, extra care must be taken to
avoid them.

What differentiates Kotlin from many other modern JVM languages such as Scala,6 Ceylon,7 and
Clojure8 is that it focuses on enterprise software and is not an academic endeavor. Instead, it is
developed and maintained by JetBrains, who make heavy use of Kotlin themselves to develop
their stack of IDEs and other tools.

Additionally, when compared to a language like Scala, Kotlin is more lightweight because it is
intentionally constrained. There are several reasons for this. First, this improves toolability because
fewer language concepts need to be supported by the compiler, IDEs, and other tools. Second, it
increases simplicity, thus making the language easier to use and code easier to read for developers.
Third, solution paths are constrained, thus making it clearer how to solve a particular problem.

5. An API is a set of well-defined interfaces for reuse.


6. https://scala-lang.org/
7. https://www.ceylon-lang.org/
8. https://clojure.org/
Why Use Kotlin on Android? 5

Why Use Kotlin on Android?


At the Google I/O 2017 developer conference, Google9 announced official support for Kotlin as a
language for Android app development; it joins Java and C++. This decision was certainly moti-
vated by Google’s lawsuit with Oracle over patents on the Java APIs, but this section focuses on
why Kotlin may be preferable to Java from a language perspective.

Kotlin can run on the JVM and is fully compatible with Java 6, which makes it a viable language
for writing Android apps. Due to its similarity with Java, Android developers can learn Kotlin’s
syntax and understand its semantics quickly. This not only means that the effort involved in
switching to Kotlin is comparably low, it also means that it entails less risk. Performance-wise,
using Kotlin instead of Java comes at no additional cost; the bytecode is executed just as fast as
any Java bytecode. Additionally, the Kotlin runtime is fairly small, so it does not add much weight
to the application.

Java on Android
To understand how Kotlin fits into the Java and Android ecosystems, we briefly recap the history
and current situation for Android app development with Java. We focus on Java 6 and above
because that is what is currently relevant for Android.

Java 6 already supports many crucial language features it inherits from Java 5, including generic
types, enumeration types, annotations, variable-argument parameters, for-each loops, and static
imports. Additionally, Java 6 itself adds significant performance improvements in the core plat-
form, the compiler, and synchronized code. Generally, each language update entails performance
improvements, a more robust core platform, and various language enhancements. Therefore,
adopting a newer Java version typically improves productivity and allows writing more concise
and maintainable code. Unfortunately, this is not always possible if a target platform (or Android
device in this case) is not compatible with the desired Java version, which is why Android devel-
opers still have to rely mostly on Java 7 at the time of writing.

In order to support at least 96% of Android devices today, you have to target Android 4.4 (API
level 19). To reach at least 98% support, Android 4.2 (API level 17) must be targeted; and to
support 100% of devices, it is necessary to target Android 2.3.3 (API level 10) from 2010. You can
find the latest numbers in Android’s Distribution Dashboard.10

On any device running Android 4.4 or lower, Android applications are run on the Dalvik virtual
machine, which is part of the operating system. Java bytecode is not executed directly but first
translated to a Dalvik executable. Since Android 4.4, Dalvik has been replaced by the Android
Runtime (ART), which is the only included runtime in Android versions 5.0 and above. Apart
from the runtime, which Java features are available also depends on which toolchain is used.
For instance, the D8 dexer that replaced DX in Android Studio 3.1 allows you to use some Java 8
features such as lambda expressions. For these two reasons (runtime and toolchain), there is no
direct mapping from Android versions to supported Java versions; each language feature may or

9. https://www.google.com/intl/en_de/about/
10. https://developer.android.com/about/dashboards/
6 Chapter 1 Introducing Kotlin

may not be supported by ART or your toolchain. Over time, additional Java language features
become incorporated into Android but with a significant delay. With Kotlin, you can have
language features even beyond those from Java 8 today.

Kotlin on Android
When developing apps for Android nowadays, you are mostly tied to Java 7. With Android Studio
3.0 and later, all Java 7 features and several Java 8 features, most prominently lambda expres-
sions, method references, and default interface methods, are available for all API levels. Still, the
more devices you want to support, the fewer language features are available and the harder it
becomes to write maintainable and high-quality code. In addition, you will always have to wait
for new Java features to become supported on Android. For instance, Java 8 Streams only work
with API level 24+ at the time of writing, whereas Kotlin’s equivalent Sequences work irrespective
of API level.

With Kotlin, you are not tied to any Java version, and you can use all features developers are
longing for in Java 8—plus many more. Functional-style programming with higher-order func-
tions and lambda expressions is incorporated from the start in Kotlin. This leads to several syntac-
tic advantages in the language design when compared with lambdas in Java. Powerful APIs for
collections and I/O operations in Kotlin supersede what you could achieve with Streams in Java 8,
and default interface methods are a part of Kotlin as well.

Kotlin versus Java 8


Java 8 was a huge language update that really changed the way you write code in Java. This is not
the case to that extent for every Java language update. For instance, Java 9 mainly changes the
way developers package and deploy their code (due to the module system) but not so much how
they think about problems and write code. As you may have noticed from the language concepts
discussed above, Kotlin has several useful language features that Java 8—and even Java 9, 10, or
11—do not provide. This includes nullable types to provide null safety, comprehensive type infer-
ence, extension functions, smart casts, named and default parameters, and more. Most of these
can make your code more concise and expressive at the same time; features like these will be
introduced in Part I of this book.

With Kotlin, you have a strong standard library at your disposal in addition to the Java standard
library, which is also available for use. The Kotlin standard library encompasses many functional-
style methods to work with collections and I/O streams, many useful predefined extension
functions, and a variety of third-party libraries to simplify working with JavaFX,11 Android,12
databases,13 and more. In this book, you will learn how to use the Anko library for Android app
development.

11. TornadoFX, https://github.com/edvin/tornadofx


12. Anko, https://github.com/Kotlin/anko
13. Exposed, https://github.com/JetBrains/Exposed
Business Perspective 7

Tool Support and Community


Kotlin is designed to be highly toolable from its roots. For instance, static typing enables sophisti-
cated refactorings, compile-time checks, and autocompletions. It is not surprising that toolability
is not an afterthought in Kotlin, considering that JetBrains is a tool company. IDEs that have
strong support for Kotlin include IntelliJ IDEA, Android Studio, Eclipse, and NetBeans. They facili-
tate writing idiomatic Kotlin code by suggesting better ways to rewrite parts of the code, among
other things.

Still, myriad tools, libraries, and frameworks has been developed for Java over the last decades,
and not all of them work with Kotlin as smoothly as they do with Java, such as static analysis
tools and linters, the build tool Buck,14 or the mocking library Mockito.15 Fortunately, most exist-
ing tools and libraries work smoothly with Kotlin, for instance Gradle,16 Retrofit,17 and Android
architecture components. On Android specifically, Google is pushing Kotlin-specific APIs, such as
Android KTX,18 that help developers write code more effectively.

There is an active community around Kotlin that acts via various channels in order to give feed-
back to the Kotlin team and help shape the way the language evolves. The Slack channel19 has
very active discussions and is a good place to ask your questions while learning Kotlin. There is
also a discussion forum20 that often features more advanced discussions concerning language
design decisions and the future direction of Kotlin. Among others, there is also a Reddit subreddit
specifically for Kotlin21 as well as various talks about the language. An overview of all community
resources is available on the Kotlin website.22

Business Perspective
Because the intent is for Kotlin to be usable wherever Java runs, it is important to make the transi-
tion easy. Indeed, the effort involved in learning Kotlin is comparably low for developers using
Java or a similar language because many language concepts are the same or very similar, such as
classes, interfaces, and generics. However, other language concepts may not be familiar to devel-
opers using a language without functional programming features, such as lambda expressions
and streams. Lastly, some language concepts are not known from most other languages, such as
non-nullable types and extension functions.

This is one of the common reasons why adopting a new language always introduces a certain
risk. You have to expect a productivity slowdown at least in the beginning of introducing a new

14. https://buckbuild.com/
15. https://site.mockito.org/
16. https://gradle.org/
17. http://square.github.io/retrofit/
18. https://developer.android.com/kotlin/ktx
19. http://slack.kotlinlang.org/
20. https://discuss.kotlinlang.org
21. https://www.reddit.com/r/Kotlin/
22. https://kotlinlang.org/community/
8 Chapter 1 Introducing Kotlin

language before productivity can finally increase. During this transition time, businesses must
also provide the time and resources necessary for the training that developers will need.

Additionally, adoption of a new language is inherently hard to reverse. If you decide to make
the transition from Java by converting several Java files from your flagship product to Kotlin and
make changes to that code, it will take time to rewrite (or decompile and refactor) this code back
to Java. Ideally, try out Kotlin in a noncritical environment such as a pet project to familiarize
yourself with the syntax and tooling without introducing unnecessary risk. Chapter 10, Migrating
to Kotlin, discusses best practices for adopting Kotlin in detail.

Tooling for Kotlin includes IntelliJ IDEA and Android Studio, where the Kotlin plugin comes with
a Java-to-Kotlin converter, which can be used for two main purposes. First, it allows the developer
to move a Java code base to Kotlin faster. However, the converted code will typically need manual
changes to be free of errors and more extensive work to resemble idiomatic Kotlin code. Second,
the converter can be used for learning purposes. Since developers understand their Java code, they
can compare that to the Kotlin code generated in order to learn how the different concepts and
language elements are mapped to one another. This is another reason why Java developers can
learn Kotlin fairly quickly. Remember, you may want to keep a copy of the original Java file.

Once developers are familiar with the Kotlin language, performance can potentially increase
significantly due to the possibility to express more with less code and the fact that Kotlin enforces
many best practices by design. For instance, correctly using nullable types increases robustness
of the code by preventing NullPointerExceptions. Similarly, classes are closed for inheritance
by default, thus enforcing the principle to explicitly design for inheritance before allowing it. In
total, this leads to a code base that is less prone to errors and costs less to maintain.

Who’s Using Kotlin?


Hundreds of companies have already incorporated Kotlin into their technology stack, from young
startups to large corporations. Besides JetBrains and Google, who obviously use them intensively,
these include Pinterest,23 Slack,24 Uber,25 Netflix,26 WordPress,27 Udacity,28 Evernote,29 Lyft,30
and Trello.31 Developers from many of these contribute to the Kotlin community by sharing their
experiences with the language and what they found to be best practices.

23. https://www.pinterest.com/
24. https://slack.com/
25. https://www.uber.com/
26. https://www.netflix.com/
27. https://wordpress.com/
28. https://udacity.com
29. https://evernote.com/
30. https://www.lyft.com/
31. https://trello.com/
Summary 9

Although Kotlin gained popularity through being an official language for Android develop-
ment, its goal is to be deployable virtually everywhere. There are three compilation targets of
the language that indicate the platforms on which Kotlin can run:
 Kotlin/JVM, which targets the JVM and Android by compiling Kotlin code to Java
bytecode. This allows Kotlin to be used anywhere Java is used, including in the backend,
on the server, on Android, or for desktop applications. This book covers Kotlin/JVM,
with a particular focus on Android, but the chapters introducing Kotlin itself are useful
irrespective of the platform on which you want to use it.
 Kotlin/JS, where Kotlin compiles to JavaScript code to target web applications and
anything that runs in the browser. Here, Kotlin interoperates with JavaScript similarly
to how it interoperates with Java on the JVM.
 Kotlin/Native, which allows Kotlin to compile to native binaries that don’t require any
virtual machine like the JVM. This enables the use of Kotlin to develop iOS apps and
embedded systems in general, and it interoperates with other native code as well.

This is the big picture the Kotlin team is aiming for. Work needs to be done in all of these
branches, especially Kotlin/Native. Still, Kotlin is a stable language that already offers many
benefits over languages like Java, JavaScript, or C.32 This book focuses on its benefits for Android
app development.

Summary
Being a statically typed language with object-oriented as well as functional language elements,
Kotlin is similar to Java 8 in many regards. However, additional language features allow the avoid-
ance of lots of boilerplate code, thus increasing conciseness and readability. Kotlin is not an
academic project and does not try to invent anything new, but instead combines what is known
to work well to solve real problems in large-scale software development.

Kotlin runs on the JVM and can be used anywhere Java is used today without losing performance
or increasing the runtime significantly. Its tool support and focused libraries, such as the
Anko library for Android, facilitate many development tasks. Java and Android developers can
typically get up to speed with Kotlin quickly and then use all mentioned features that go beyond
Java 8, 9, 10, or even 11.

32. C Programming Language, http://www.bell-labs.com/usr/dmr/www/chist.html


This page intentionally left blank
2
Diving into Kotlin

When you catch a glimpse of your potential,


that’s when passion is born.
Zig Ziglar

In this chapter, you’ll familiarize yourself with the syntax, features, and underlying concepts of
the Kotlin programming language. Apart from basics like data types and control flow, you’ll learn
how to avoid null in Kotlin, how to create different types of functions, how to check for equality
of two variables, and how to handle exceptions.

Kotlin REPL
To try out simple code snippets, Kotlin provides a read-eval-print-loop (REPL). The REPL evaluates
a given piece of code directly and prints its result. This is useful to quickly try out how to use a
language feature or a third-party library or to share code snippets with colleagues. Thus, it speeds
up the feedback loop and helps learn the language faster.

With the Kotlin plugin activated, every IDE should contain the Kotlin REPL. In Android Studio
and IntelliJ, you can launch it from the menu under Tools, then Kotlin, and then Kotlin REPL.
Alternately, you can create Kotlin script files (with .kts as file extension) in an IntelliJ project1 to
run the code examples from this and the following chapters. The REPL and script files allow you
to run your code without a main function. Note that you have to adjust the file extension manu-
ally to .kts if you create a regular Kotlin file with .kt extension. Lastly, you can use the online
editor on the Kotlin website,2 but you must then write your code inside a main function.

1. Create a Kotlin/JVM project via File, New, Project…, and choose Kotlin/JVM.
2. https://try.kotlinlang.org/
12 Chapter 2 Diving into Kotlin

Note
All listings in this book are written as if they were inside a Kotlin script file, ending in .kts as
opposed to .kt. For the sake of brevity, some listings only illustrate a point but may require
additional code to actually run. But most are runnable as a script as shown.
In any case, a runnable full-fledged version is available from the GitHub repository.3 Additionally,
the book’s companion website allows you to run all listings directly inside your browser.4

Variables and Data Types


This section covers variable declarations, basic data types, how they are mapped to Java types in
the bytecode, and how you can use type inference to write more concise code.

Variable Declarations
In Kotlin, there are two keywords to declare a variable. The first is called var and creates a
mutable variable, meaning that you can reassign the variable to a new value any number of times,
as illustrated in Listing 2.1.

Listing 2.1 Declaring Mutable Variables


var mercury: String = "Mercury" // Declares a mutable String variable
mercury = "Venus" // Can be reassigned because mutable

In Kotlin, the var keyword stands in front of the variable name, followed by a colon, the variable
type, and the assignment. You’ll learn to write this more concisely later using type inference.

Kotlin promotes the use of immutability wherever appropriate, so you should use the val
keyword whenever possible to create read-only variables as in Listing 2.2.

Listing 2.2 Declaring Read-Only Variables


val mercury: String = "Mercury" // Declares a read-only variable
mercury = "Venus" // Compile-time error: "val cannot be reassigned"

This way, the variable cannot be reassigned after its declaration, thus the compile-time error
when trying to reassign mercury to a different value. For local variables like this, you can also
split up declaration and initialization. In any case, once a read-only variable is assigned a value, it
cannot be reassigned.

3. https://github.com/petersommerhoff/kotlin-for-android-app-development
4. https://www.kotlinandroidbook.com/listings
Variables and Data Types 13

Note
Whether the variable itself is mutable (can be reassigned) or read-only (can only be assigned
once) has nothing to do with whether the object stored inside the variable is mutable.
For instance, Listing 2.1 presents a mutable reference to an immutable string object.
Similarly, you could have a val referencing a mutable object. Thus, just using val is not
enough to take advantage of immutability in your code—the referenced object must also be
immutable.

Immutability Matters
Kotlin promotes immutability both in the way the language is designed and in its communica-
tion of coding conventions. This is because Kotlin focuses on building large software systems
by bringing together best practices that proved beneficial in the industry—one of which is
immutability.

Immutable variables eliminate many common sources of errors, especially in multithreaded code
such as on Android, because side effects and unwanted data manipulations are not possible for
immutable variables, leading to more robust code that is also easier to reason about. Immutability
is covered in detail in Chapter 3, Functional Programming in Kotlin, because it is one of the
underlying principles of functional programming.

Basic Data Types


The basic data types in Kotlin are similar to those from other languages. They are not considered
primitive types because they are objects at runtime. An overview of basic types in Kotlin, their
memory usage, and their range is given in Table 2.1.

Table 2.1 Data Types in Kotlin


Data Type Bits Range
Boolean 1 { true, false }

Byte 8 -128..127

Short 16 -32768..32767

Int 32 -2147483648..2147483647

Long 64 -18446744073709551616..18446744073709551615

Float 32 1.4e-45..3.4028235e38

Double 64 4.9e-324..1.7976931348623157e308

Char 16 16-bit Unicode characters

String 16 * length Unicode strings


14 Chapter 2 Diving into Kotlin

All number types represent signed numbers,5 meaning the range includes negative and positive
numbers. Also, all strings are immutable, so if a mutable string variable gets reassigned, a new
String object is created.

Keep in mind that floating point numbers invariably have limited precision. For instance, while
0.000000001 – 0.00000000005 evaluates correctly to 9.5E-10, an additional zero causes a
result of 9.500000000000001E-11. Thus, never compare Float and Double values for equality
but rather compare with an adequate tolerance. Also, treat computation results with a grain of salt
as imprecisions may have led to numerical errors.

Mapping of Basic Types


When using Kotlin for Android development, the code will end up as Java bytecode. Thus, it is
important to understand how the basic types are mapped between Kotlin and Java.
 When receiving data from Java, primitive Java types will be mapped to their equivalent
Kotlin type at compile-time, for example char to kotlin.Char and int to kotlin.Int.
This allows you to work with primitive values coming from Java the same way as with
Kotlin’s own types.
 Conversely, at runtime, Kotlin’s basic types are mapped to their Java counterparts, for instance
kotlin.Int to int and kotlin.Double to double. This is possible because Kotlin’s types
are by default non-nullable, meaning they can never be null. Hence, they can always be
mapped to a primitive Java type instead of a boxed type such as java.lang.Integer.

There are more rules for mapped types that are introduced once you know more of Kotlin’s
language features and libraries.

Type Inference
One of the language features of Kotlin that is used throughout idiomatic code is type inference.
This allows you to skip the type in variable declarations whenever the Kotlin compiler can infer it
(which it often can). Thus, you can declare variables as in Listing 2.3.

Listing 2.3 Type Inference


val mercury = "Mercury" // Inferred type: String
val maxSurfaceTempInK = 700 // Inferred type: Int
val radiusInKm = 2439.7 // Inferred type: Double

The first statement is semantically equivalent to the variable declaration in Listing 2.2, where the
type String was specified explicitly. With type inference, you are able to write more concise code
that focuses on what is relevant—and in cases where the type may not be clear to the reader this
way, you can still explicitly specify it. There is also an option in Android Studio to show inferred
types, which is especially useful when starting out.

5. Kotlin 1.3 introduced additional unsigned integer types like UShort, UInt, and ULong.
Conditional Code 15

Note that in Kotlin, you cannot pass an Int into a function that expects a Float or Double as
you can do with primitive types in Java. Kotlin does not automatically convert between these
types. However, the standard library has helpers like toInt, toDouble and so forth that you can
use for these conversions.

Java Note
Java 10 introduced local-variable type inference with Java Development Kit (JDK) Enhancement
Proposal (JEP) 286.6 Before that, Java only supported type inference for generic type param-
eters with the diamond operator (since Java 7).7

Conditional Code
In Kotlin, if and when are used for conditional control flow. Both are expressions, meaning that
they have a value. One consequence is that they can be assigned to a variable. But you can also
ignore their value, effectively using them like statements (which don’t carry a value).

If and When as Statements


For conditional control flow, Kotlin provides the keywords if and when. These allow running
certain parts of your code only if a given condition is fulfilled. The syntax for if conditions is the
same as in languages like C or Java, and the when keyword is similar to switch in these languages,
but more powerful. Conditions with if can be written as in Listing 2.4.

Listing 2.4 Conditions with if


if (mercury == "Mercury") {
println("Universe is intact.")
} else if (mercury == "mercury") {
println("Still all right.")
} else {
println("Universe out of order.")
}

The else-if and else branches are optional. Conditions can be composed using && (“and”)
and || (“or”), and ! (“not”) can be used to negate a condition. If a condition is composed of
multiple atomic conditions using && or ||, each of them is evaluated lazily. In the condition
a && b for instance, the second atomic condition b is not evaluated if a is already evaluated to
false because then the whole condition must be false. This is called short-circuiting and aims to
improve performance.

6. http://openjdk.java.net/jeps/286
7. https://docs.oracle.com/javase/7/docs/technotes/guides/language/type-inference-generic-instance-
creation.html
16 Chapter 2 Diving into Kotlin

In addition to if, Kotlin also features powerful when conditions. These can be used to define
different behavior for a given set of distinct values. In this way, when conditions are a more
concise alternative to writing cascades of conditions. Thus, you can replace the if condition from
Listing 2.4 with a when condition as in Listing 2.5 to save two lines of code (~29%) compared with
cascading else-if branches.

Listing 2.5 when for Cascades of Conditions


when (mercury) {
"Mercury" -> println("Universe is intact.")
"mercury" -> println("Still all right.")
else -> println("Universe out of order.")
}

First, the variable to test against is passed into parentheses and the when block is enclosed by curly
braces, followed by each case with a condition on the left-hand side and its associated code on the
right-hand side, separated by an arrow (->). If the left-hand value matches the variable value, its
corresponding right-hand side is executed and no further cases are probed.

Although similar to switch from other languages, this is more powerful because it can perform
more types of checks, as shown in Listing 2.6.

Listing 2.6 Types of Conditions for when


when(maxSurfaceTempInK) {
700 -> println("This is Mercury’s maximum surface temperature") // 1
0, 1, 2 -> println("It’s as cold as it gets") // 2
in 300..699 -> println("This temperature is also possible on Mercury") // 3
!in 0..300 -> println("This is pretty hot") // 4
earthSurfaceTemp() -> println("This is earth’s average surface temperature") // 5
is Int -> println("Given variable is of type Int") // 6
else -> { // 7
// You can also use blocks of code on the right-hand-side, like here
println("Default case")
}
}

This demonstrates that you can combine different kinds of checks inside the same when condition.
Let us go through these one by one.

1. The first case inside the when block checks whether maxSurfaceTempInK equals 700.

2. The second case checks if at least one of several comma-separated values matches the
variable. If so, the right-hand side is executed.
Conditional Code 17

3. The third condition checks if the given variable is in the range of numbers from 300 to
699 (both bounds inclusive) using the in keyword. The range is created using Kotlin’s
shorthand operator n..m that internally calls n.rangeTo(m), which returns an IntRange.
In Android Studio, you can go to the declaration of such operators just like any other
function to see what it does.

4. The fourth condition is similar but checks whether the variable is not in a given range.

5. In the fifth case, you see that it is also possible to call functions on the left-hand side to
specify the value against which to test. If the function would return an iterable object or a
range, you can use it with in, for instance in earthSurfaceTempRange() -> …

6. The sixth condition checks the variable type, in this case whether it’s an Int. Type
checking is discussed in more detail in Chapter 4, Object Orientation in Kotlin.

7. Lastly, the else keyword is used to define the default case that is executed only if none
of the previous conditions could be evaluated to true. As you can see, it is possible to
define a larger code block on the right-hand side for each case by using curly braces, as
demonstrated in Listing 2.6. If you do, the last line then defines the value of the when
expression, as explained in the following section covering conditional expressions.

Note
Notice that there are no break statements; after the first case matches, its right-hand side is
executed and no more subsequent cases are checked. Thus, the code behaves as if there was
an implicit break statement included at the end of each case, preventing the common mistake
of forgetting a break statement.

To replace arbitrary cascades of conditions, you can omit the variables in parentheses and then
use any Boolean expressions on the left-hand sides, as in Listing 2.7.

Listing 2.7 Arbitrary Boolean Conditions with when


when {
age < 18 && hasAccess -> println("False positive")
age > 21 && !hasAccess -> println("False negative")
else -> println("All working as expected")
}

Conditional Expressions
In Kotlin, both if and when are expressions, meaning that they have a value. If that value is not
used, they are equivalent to statements, which carry no value. However, using them as expres-
sions is a powerful feature that can, for instance, be used to avoid null in some cases.

In both if and when expressions, the value of each case (and therefore the whole expression) is
defined by the last line in the corresponding code block. Thus, Listing 2.5 can be rewritten to use
if as an expression as in Listing 2.8.
18 Chapter 2 Diving into Kotlin

Listing 2.8 If Expression


val status = if (mercury == "Mercury") { // ‘if’ is an expression (it has a value)
"Universe is intact" // Expression value in case ‘if’ block is run
} else {
"Universe out of order" // Expression value in case ‘else’ block is run
}

Here, the if expression’s value is assigned to a status variable. The println statements are
removed in order to return the string value instead of printing it right away. The else-if branch
is removed for the sake of brevity but any number of else-if branches can be used. Notice the
use of type inference by leaving out the type declaration of status. In case the if block and
else block have different types, Kotlin currently infers Any as the overall type of the if expres-
sion. Any is the superclass of all other (non-nullable) classes in Kotlin.

Because if is an expression in Kotlin, there is no extra ternary conditional operator (result =


condition ? thenThis : elseThis) because if expressions fulfill this purpose. Consider
Listing 2.9 as a more concise version of Listing 2.8.

Listing 2.9 Ternary Conditional Operator with if


val status = if (mercury == "Mercury") "Intact" else "Out of order"

Note
The above way to use if as a ternary conditional operator uses the fact that curly braces are
optional for a branch if it only consists of a single expression, plus the fact that line breaks are
also optional. It works the same way when introducing line breaks or adding curly braces.
One conclusion of this is that this structure may also be used with an arbitrary number of
else-if branches. However, this is uncommon because it quickly becomes unreadable.

When expressions are very similar: The last line in each case block defines the corresponding value
of the when expression if that block is executed. For instance, you can rewrite Listing 2.6 to use
strings on the right-hand sides so that the when expression has a string value that can be assigned
to a variable, as shown in Listing 2.10.

Listing 2.10 Using when as an Expression


val temperatureDescription = when(maxSurfaceTempInK) {
700 -> "This is Mercury’s maximum surface temperature"
// ...
else -> {
// More code...
"Default case" // Expression value if ‘else’ case is reached
}
}
Loops and Ranges 19

Here, the expression value for each case can either be directly defined on the right-hand side if no
additional code should be executed, or it can be defined in the last line of a code block as in the
else branch.

Loops and Ranges


For repetitive control flow, Kotlin provides for loops, while loops, and do-while loops. While
and do-while loops work the same way as in Java, whereas for loops are similar to foreach loops
known from many other languages.

While Loops
While loops and do-while loops are used to repeat a block of code as long as a given condition
is true. The condition is defined in parentheses following the while keyword, as portrayed in
Listing 2.11.

Listing 2.11 while Loop


val number = 42
var approxSqrt = 1.0
var error = 1.0

while (error > 0.0001) { // Repeats code block until error is below threshold
approxSqrt = 0.5 * (approxSqrt + number / approxSqrt)
error = Math.abs((number - approxSqrt*approxSqrt) / (2*approxSqrt))
}

In the first example, the while loop keeps repeating its block of code as long as its condition is
true, that is, as long as the error is still greater than 0.0001. The code approximates the square
root of a given number with the Babylonian method up to a tolerance of 0.0001. Note that
java.util.Math can be used seamlessly.

The point of the do-while loop is that it executes its code block at least once initially, and only
then checks its condition. Listing 2.12 demonstrates a common use case for this type of loop.

Listing 2.12 do-while Loop


do {
val command = readLine() // Reads user command from console
// Handle command...
} while (command != ":quit") // Repeats code block until user enters ":quit"

This reads user input, for example in a command-line tool, until the user wants to quit. Generally,
do-while loops are used if you want to guarantee at least one initial iteration.
20 Chapter 2 Diving into Kotlin

For Loops
The for loop in Kotlin resembles foreach loops known from other languages and can be used to
iterate over any type that provides an iterator. Most prominently, this includes ranges, collections,
and strings, as demonstrated in Listing 2.13.

Listing 2.13 for Loops


for (i in 1..100) println(i) // Iterates over a range from 1 to 100

for (i in 1 until 100) println(i) // Iterates over a range from 1 to 99

for (planet in planets) // Iterates over a collection


println(planet)

for (character in "Mercury") { // Iterates over a String character by character


println("$character, ")
}

The first line uses the syntax 1..100 to create an IntRange to iterate over, as you saw in when
expressions. Thus, the first loop prints the elements from 1 to 100. More versatile uses of ranges in
for loops are discussed in the next subsection. The second example uses until to create a range
where the upper bound is exclusive. The third loop iterates over a collection of planets; collec-
tions are discussed in detail in Chapter 3. Lastly, you can use the for loop to iterate over strings
character by character.

Note that the last for loop shows another interesting feature of Kotlin, called string interpolation.
This means that you can insert the value of a variable into a string by prefixing the variable with
a $ sign as in $character. More complex expressions must be separated from the surrounding
string by curly braces, for instance "Letter: ${character.toUpperCase()}".

Using Ranges in for Loops


After the initial example of for loops with ranges, you may be wondering how to iterate over
more complex structures of elements, such as the numbers from 100 down to 1, maybe even in
steps of 5. Kotlin provides the helper functions downTo and step to easily create such ranges, as
shown in Listing 2.14.

Listing 2.14 Ranges in for Loops


for (i in 100 downTo 1) println(i) // 100, 99, 98, …, 3, 2, 1
for (i in 1..10 step 2) println(i) // 1, 3, 5, 7, 9
for (i in 100 downTo 1 step 5) println(i) // 100, 95, 90, …, 15, 10, 5
Functions 21

The syntax here is interesting because it uses infix functions. Calling 100 downTo 1 is equiva-
lent to calling 100.downTo(1). This is possible because downTo is declared as an infix function,
so that the function can stand between its two arguments. Similarly, 1..10 step 2 is equivalent
to (1..10).step(2) and the third range could be rewritten as 100.downTo(1).step(5). Infix
functions thus allow writing more readable code with less clutter from parentheses. Notice that
infix functions must have two parameters. The next section covers how to create and work with
your own functions, including infix functions.

Functions
Functions are a powerful language feature in Kotlin. This section starts with the basics of creating
and calling functions, and then shows how to use default values for parameters and how to define
special types of functions such as extension functions, infix functions, and operator functions.

Function Signatures
A function’s signature is the part of a function declaration that defines its name, parameters, and
return value. Together with a function body, it defines the function completely. In Kotlin, a func-
tion declaration is denoted as in Listing 2.15.

Listing 2.15 Declaring a Function


fun fib(n: Int): Long {
return if (n < 2) {
1
} else {
fib(n-1) + fib(n-2) // Calls ‘fib’ recursively
}
}

This is an (inefficient) implementation of the well-known Fibonacci sequence. Here, fun fib(n:
Int): Long is the function signature. It begins with the fun keyword used to define functions
in Kotlin, followed by the function name, its parameters in parentheses, a colon, and the return
value. Notice the consistency with variable declarations in Kotlin, where the type comes after the
name as well.

In the function body, an if expression is used instead of placing a return in each branch. This
is not necessary but would allow you to write the function body in a single line by using it like a
ternary conditional operator. You’ll see this in the next subsection.

The fib function can be called by writing the function name with its arguments in parentheses,
as in Listing 2.16. This would also work if you declared fib inside a regular Kotlin file (not a script
file) but outside of a main function. Then the fib function is a top-level function—meaning that
it is declared directly on file level, not inside any other declaration (such as a class or another func-
tion). In this book, the terms top-level and file-level are used synonymously.
22 Chapter 2 Diving into Kotlin

Listing 2.16 Calling a Function


val fib = fib(7) // Assigns value 21L (‘L’ indicates Long)

The value of the function call is stored in a variable called fib that implicitly has the type Long.
Notice that the variable and function names do not clash because their use can always be differ-
entiated by the compiler due to the parentheses used in function calls.

Note
For this section, keep in mind that the term parameter refers to what is used at the declaration-
site as part of the function signature, whereas arguments are the actual values used at the
call-site. So, in the declaration of fib in Listing 2.15, n is a parameter of type Int and 7 is the
corresponding argument in Listing 2.16.

Shorthand for Single-Expression Functions


For single-expression functions, Kotlin provides a shorthand that looks almost like a variable
declaration. Let’s use this to simplify the fib function in two steps. Listing 2.17 first reduces the
expression in the function body to a single expression (and a single line).

Listing 2.17 Transforming the Function Body into a Single Expression


fun fib(n: Int): Long {
return if (n < 2) 1 else fib(n-1) + fib(n-2) // Uses ‘if’ as a ternary operator
}

With this, you can remove the return and directly assign the expression as the value of the func-
tion, as in Listing 2.18.

Listing 2.18 Shorthand Function Notation Using Equals Sign


fun fib(n: Int): Long = if (n < 2) 1 else fib(n-1) + fib(n-2)

Here, fib uses Kotlin’s shorthand notation by defining the function body using an equals sign.
This works for functions that contain only a single expression because you can then assign the
expression value as the value of the function. The explicit return type is still required here because
type inference would run into infinite recursion. But in general, you can make use of type infer-
ence for the return type when using this shorthand notation.

Main Function
The special main function defines the entry point into a program. The easiest way to create one
is to use a top-level function as in Listing 2.19. This must be done in a regular Kotlin file, not
a script file.
Functions 23

Listing 2.19 Declaring a Main Function


fun main(args: Array<String>) { // A main function must have this signature
println("Hello World!")
}

The main function is declared like any other top-level function and accepts an array of strings
that captures command-line arguments. The missing return type indicates that this is a void func-
tion. In Kotlin, void is represented by the Unit type to ensure that every function call has a value
and can, for instance, be assigned to a variable. Unit is the default return type, so if you don’t
define an explicit return type for a function, it returns Unit (like the main function).

Note
A main function allows you to run code such as print statements in normal Kotlin files (with
.kt file extension) instead of using Kotlin scripts (.kts files). Listings in this book still
assume a script file and therefore don’t use a main function unless noted otherwise.

Default Values and Named Parameters


In Kotlin, you can define default values for function parameters. This makes them optional, so
if no argument is given for such a parameter, its default value is used. In Listing 2.20, the default
value for the recursion depth is zero.

Listing 2.20 Defining Default Values for Parameters


fun exploreDirectory(path: String, depth: Int = 0) { … } // Default depth is zero

The parameter definition syntax is similar to variable declarations but without val or var
keywords. Also, the types cannot be omitted because they form part of the function contract and
must be well defined.

This function can now be called in several different ways because an argument for the second
parameter is optional, and you can make use of named parameters to assign only selected param-
eters or to change their order. This is demonstrated in Listing 2.21.

Listing 2.21 Using Named Parameters


val directory = "/home/johndoe/pictures"

exploreDirectory(directory) // Without optional argument, depth is 0


exploreDirectory(directory, 1) // Recursion depth set to 1
exploreDirectory(directory, depth=2) // Uses named parameter to set depth
exploreDirectory(depth=3, path=directory) // Uses named parameters to change order
24 Chapter 2 Diving into Kotlin

In these examples, a directory on the file system is explored. The first call to exploreDirec-
tory uses the default depth of zero by omitting the optional argument. The second call defines
all arguments as usual, whereas the third one uses named parameters to set the depth. This is not
required here but may increase readability. In contrast, because the last call uses named param-
eters to change the order of arguments passed in, all parameters must be explicitly named. Also,
for functions with multiple default values, named parameters allow passing in any subset of the
optional parameters.

Note
Parameters with default values should be placed at the end of the parameter list. That way,
all required arguments can be passed in first without any ambiguity about which parameter
belongs to which value. If you have multiple optional parameters of the same type, their order
decides which value belongs to which.
In any case of ambiguity, named parameters can be used to resolve it.

Overloading
Functions can be overloaded by creating another function with the same name but different
parameters. A typical use case for this is when there are alternate ways to represent the data the
function requires, as illustrated in Listing 2.22.

Listing 2.22 An Overloaded Function


fun sendEmail(message: String) { … }
fun sendEmail(message: HTML) { … }

This way, the sendEmail function can be called either by passing in a string to send a plain text
email, or by passing in some HTML object to send an email with HTML content.

Java Note
A common reason to use overloading in languages without default parameter values (like Java)
is to implement optional parameters. This is done by adding overloads that omit an optional
parameter and delegate to the full-fledged implementation with a default value passed in.
Thanks to default parameter values, there is no need to use function overloading for this in
Kotlin, which can drastically reduce the number of code lines.

// Java code
Pizza makePizza(List<String> toppings) {
return new Pizza(toppings);
}
Pizza makePizza() {
return makePizza(Arrays.asList());
}
Functions 25

Because Kotlin provides default parameter values, there is no need to use function
overloading for this:
fun makePizza(toppings: List<String> = emptyList()) = Pizza(toppings)

This drastic reduction in lines of code improves exponentially with the number of optional
parameters.

Extension Functions
From other languages, you may be familiar with utility classes such as StringUtils or
DateUtils that define a variety of helper functions for strings or dates, respectively. Their inten-
tion is to extend the interface (API) of existing classes with additional functions that are useful
either in your application context or even in general. In many languages, this is necessary for
classes you don’t own because you cannot change the actual classes.

In Kotlin, extension functions open up the possibility to add methods and properties directly
to existing classes such as Int or Date, or at least Kotlin makes it look like you could (classes are
discussed in detail in Chapter 4). Consider Listing 2.23 as an example of an extension function.

Listing 2.23 Creating and Calling Extension Functions


fun Date.plusDays(n: Int) = Date(this.time + n * 86400000) // Adds n days to date

val now = Date()


val tomorrow = now.plusDays(1) // Extension can be called directly on a Date object

To create an extension function, you use the fun keyword as usual but prefix the function name
with the so-called receiver class. The extension can then be called like any method on the receiver
class, resulting in cleaner code than when using utility classes. Plus, your IDE can now offer useful
autocompletion, unlike when using static methods from utility classes.

Static Resolving of Extensions


Extensions cannot actually become part of existing classes; Kotlin just allows them to be called
using the same syntax. Semantically, however, they are different because they’re resolved stati-
cally rather than at runtime. Thus, an extension function print called on an expression of type
Number calls Number::print, even if the expression evaluates to the subtype Int at runtime.
Listing 2.24 demonstrates exactly this situation.

Listing 2.24 Static Resolving of Extension Functions


fun Number.print() = println("Number $this") // Extension on supertype
fun Int.print() = println("Int $this") // Extension on subtype

val n: Number = 42 // Statically resolved type: Number


n.print() // Prints "Number 42"
26 Chapter 2 Diving into Kotlin

Even though n is an Int at runtime, the extension function being called for n.print() is
resolved at compile time, thus calling Number::print instead of Int::print. So keep in mind
there is no polymorphism of this type when using extension functions, and use this feature
carefully for functions that callers might expect to behave polymorphically.

Note
If the receiver type of an extension function already has a member function with the same
signature, the member function takes precedence over the extension function.

Scope and Importing


Extension functions and properties are typically created on the top level and are therefore visible
inside the entire file. Just like any other function or variable, they can also be given a visibility.
Visibilities in Kotlin are discussed in detail in Chapter 4. For now, it is just important that the
default visibility be public so that extensions can be imported into other files as well, which
looks like static imports, as shown in Listing 2.25. Assume here that the plusDays extension
from Listing 2.23 is defined in a file residing in a package named time.

Listing 2.25 Importing Extension Functions


import com.example.time.plusDays

Note that the function name directly follows the package name because the extension does not
reside inside a class or object. If there are multiple extension functions with the name plusDays
defined in the package, all of them are imported.

Tip
Extension functions offer a convenient way to work around limitations of third-party APIs that
you cannot modify directly. For example, you can add methods to interfaces or encapsulate boil-
erplate around APIs. This is highly useful on Android, and you will see several examples of this
in Chapter 7, Android App Development with Kotlin: Kudoo App, and Chapter 8, Android App
Development with Kotlin: Nutrilicious.

Infix Functions
Normally, function calls in Kotlin use prefix notation with parentheses around the arguments.
For functions with two parameters, however, you may want to put the function name between the
arguments, similar to computations like 7 + 2 where the operator also stands between its argu-
ments. Kotlin allows you to define such infix functions and has several predefined ones. You’ve
already seen the infix functions until, downTo, and step that are commonly used in for loops. As
another example, consider the to function that is predefined in Kotlin and shown in Listing 2.26.

Listing 2.26 The Infix Function to


infix fun <A, B> A.to(that: B) = Pair(this, that) // Declared in standard library
Functions 27

A and B are generic parameters, which are discussed in Chapter 4. For now, notice the infix
modifier for the function. This allows you to call this function as shown in Listing 2.27, with the
function name between the arguments.

Listing 2.27 Calling an Infix Function


val pair = "Kotlin" to "Android" // Pair("Kotlin","Android")
val userToScore = mapOf("Peter" to 0.82, "John" to 0.97) // Creates a map

The first line creates a Pair("Kotlin", "Android"). The second example shows the most
common use case for this infix function: instantiating maps using the helper function mapOf that
accepts a vararg of type Pair and using to to create the Pair objects for the map. Although it’s
not necessary to use the to function instead of the Pair constructor, it can improve readability in
many cases.

You can also define your own infix functions using the infix modifier, but only if the function
is either a member function of a class or an extension function and has only one additional
argument (just like the to function above). For instance, you could create an infix function that
duplicates a string a certain number of times using the standard library function repeat, as in
Listing 2.28.

Listing 2.28 Defining Own Infix Functions


infix fun Int.times(str: String) = str.repeat(this)
val message = 3 times "Kotlin " // Results in "Kotlin Kotlin Kotlin "

Operator Functions
When looking at the code above, you may be thinking that it would be nicer if you could instead
just write 3 * "Kotlin "—and you can achieve this. The concept behind this is called opera-
tors or operator functions. Operators are symbols with built-in semantics for the compiler such as
+, -, or +=; and by writing your own operator functions, you can define your own semantics for
these operators. This is done by defining functions with a specific name such as plus, minus, or
plusAssign for the three operators listed above.

To achieve the more concise syntax 3 * "Kotlin ", you can use the operator function times
that is associated with the * operator, as in Listing 2.29.

Listing 2.29 Defining and Calling Operators


operator fun Int.times(str: String) = str.repeat(this)
val message = 3 * "Kotlin " // Still results in "Kotlin Kotlin Kotlin "
28 Chapter 2 Diving into Kotlin

Compared with the infix function, the only difference is the use of the operator keyword
instead of the infix keyword. Operators can increase the expressiveness and conciseness of your
code, but they should be used judiciously. The Kotlin team deliberately allows only a predefined
set of operators to be used, in contrast to Scala for instance. The reason behind this is that overus-
ing operators makes your code hard to understand for other developers (and yourself). Table 2.2
gives an overview of the most important operators available in Kotlin.

Table 2.2 Available Operator Functions


Function Name Operator Example Equivalent
Arithmetic Operators
plus + 2 + 3 2.plus(3)

minus - "Kotlin" – "in" "Kotlin".minus("in")

times * "Coffee" * 2 "Coffee".times(2)

div / 7.0 / 2.0 (7.0).div(2.0)

rem % "android" % 'a' "android".rem('a')

Assignment Operators
plusAssign += x += "Island" x.plusAssign("island")

minusAssign -= x -= 1.06 x.minusAssign(1.06)

timesAssign *= x *= 3 x.timesAssign(3)

divAssign /= x /= 2 x.divAssign(2)

remAssign %= x %= 10 x.remAssign(10)

Miscellaneous Operators
inc ++ grade++ grade.inc()

dec -- 'c'-- 'c'.dec()

contains in "A" in list list.contains("A")

rangeTo .. now..then now.rangeTo(then)

get […] skipList[4] skipList.get(4)

set […] = … array[5] = 42 array.set(5, 42)

Tip
There are more operators available that are not listed here. To get a complete list of all opera-
tor functions available for your version of Kotlin, simply use autocompletion in IntelliJ or Android
Studio. For instance, typing “operator fun Int.” and invoking autocompletion after the dot
brings up all available operators to override.
Null Safety 29

Null Safety
In 1965, Tony Hoare designed an object-oriented programming language called ALGOL W.8 As he
admitted, a null reference was added simply because it was easy to implement and thus tempt-
ing. He now calls it his billion-dollar mistake. This is not to talk down his accomplishments; he
has contributed incredibly much to the field of computer science. But null references cause a lot
of pain, as you know from the dreaded NullPointerException.

Fortunately, Kotlin allows you to control the risk of null references and even eliminate them
from pure Kotlin code. This is achieved by differentiating between nullable and non-nullable
types.

Nullable Types
Every type in Kotlin is by default non-nullable, meaning that it cannot hold a null reference.
Thus, it’s also safe to access any property or method on it, as you’ve seen in the previous code
examples in this book. To create a nullable variable, you have to append a question mark to the
variable type as shown in Listing 2.30.

Listing 2.30 Nullable and Non-Nullable Types


val name: String? = "John Doe" // Nullable variable of type ‘String?’
val title: String? = null // Nullable variables may hold ‘null’

val wrong: String = null // Causes compiler error because not nullable

The type String? denotes a nullable string, in contrast to the normal String, which cannot
hold null. You use the same syntax for all types, for instance Int? or Person?. A good practice is
to avoid them whenever possible. Unfortunately, this is not always possible when interoperating
with Java because every value coming from Java may potentially be null. Interoperability issues
are covered in more detail in Chapter 5, Interoperability with Java.

Working with Nullables


When you use a nullable variable, then in general you can no longer safely access properties and
methods on it. For this reason, Kotlin forbids normal access.

Safe-Call Operator
Listing 2.31 shows how you can safely access members of nullable types, first using explicit null
checks and then using the safe call operator.

8. https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare
30 Chapter 2 Diving into Kotlin

Listing 2.31 Accessing Members of Nullable Variables


val name: String? = "John Doe"
println(name.length) // Not safe => causes compile-time error

if (name != null) { // Explicit null check


println(name.length) // Now safe so compiler allows accessing length
}

println(name?.length) // Safe call operator ‘?’


val upper = name?.toUpperCase() // Safe call operator

You cannot access name.length directly because name may be null, thus causing a
NullPointerException. Instead, you can explicitly check for null. The compiler will track
your condition and allow direct access inside the if block because it’s safe. This concept is called
smart cast and is discussed in detail in the next section. Alternately, you can use Kotlin’s safe-call
operator by appending a question mark to the variable as in name?.length. This returns the
string length if name is not null, and otherwise returns null. Thus, the type of name?.length
is Int?, and the type of the variable upper in the above code is String?.

Note
You can define extensions on nullable types so that they can be called directly on that type,
without safe-call operator or other null handling. In fact, this is how the toString method is
defined in Kotlin. The extension is defined as Any?.toString() = ….
When defining extensions on nullable types, you must handle the null case inside the exten-
sion function to make sure it can be called safely.

Elvis Operator
In order to quickly define a default value for the case in which a variable is null, Kotlin offers the
elvis operator, which looks like a shortened form of the ternary conditional operator from other
languages and gets its name from the look of the operator when seen as an emoticon. Listing 2.32
shows an example for this operator.

Listing 2.32 Elvis Operator


val name: String? = null
val len = name?.length ?: 0 // Assigns length, or else zero as the default value

The elvis operator ?: can be read as “or else” and is a powerful tool to get rid of nullability in your
code quickly. Here, len is now an Int instead of an Int? because the elvis operator returns the
value on its right-hand side if its left-hand side evaluates to null. So here, len will be zero.
Null Safety 31

Apart from the elvis operator, Kotlin also provides a variety of useful functions to deal with
nullable types. For instance, the above result could also be achieved using name.orEmpty().
length.

Note
At this point, you could create utility functions like orEmpty yourself. In fact, orEmpty is
simply an extension function on String? that uses the elvis operator.
Navigate to the declaration of such functions in your IDE to see how they are defined; this can
help you tremendously in grasping the language.

Unsafe Call Operator


Lastly, there is also an unsafe call operator that allows you to force accessing a property or method
on a nullable type without handling null. You should use this judiciously, never as a shortcut
to handling nullability correctly. However, it has legitimate uses and you can use it when you’re
certain that an object cannot be null at a point in your code. Listing 2.33 shows how to use the
unsafe call operator; it is intentionally designed to look like you’re shouting at the compiler using
a double exclamation mark.

Listing 2.33 Unsafe Call Operator—Member Access


val name: String? = "John Doe" // May also be null
val len = name!!.length // Asserts the compiler that name is not null; unsafe access

The second line assures the compiler that name is not null at that point using name!! (which
succeeds in this case). The syntax name!! effectively transforms the nullable type to a non-
nullable type so that you can directly access any of its members. Thus, it is often used without any
member access following it, as shown in Listing 2.34.

Listing 2.34 Unsafe Call Operator—Passing Arguments


fun greet(name: String) = println("Hi $name!")

greet(name!!) // Better be sure that ‘name’ cannot be null here


greet(name ?: "Anonymous") // Safe alternative using elvis operator

Using the unsafe call operator injudiciously is one of the few ways you can force a
NullPointerException in pure Kotlin code. That nullability even exists in Kotlin is due to its
very pragmatic goal to be 100% interoperable with Java. Fundamentally, there’s no need for null
references at all, so try to avoid them whenever you can. Even when you do need them, it is good
practice to keep nullability local, for instance, by defining useful default values early on using the
elvis operator.
32 Chapter 2 Diving into Kotlin

Equality Checks
There are two fundamental ways to check equality of objects: referential equality and structural
equality. Referential equality of two variables means that they point to the same object in memory
(they use the same reference). In contrast, structural equality means that the values of two variables
are the same, even if stored at different locations. Note that referential equality implies structural
equality. Kotlin provides one operator for each of them: You use === to check for referential
equality and == to check for structural equality, as done in Listing 2.35.

Listing 2.35 Referential and Structural Equality Checks


val list1 = listOf(1, 2, 3) // List object
val list2 = listOf(1, 2, 3) // Different list object but with same contents

list1 === list2 // false: the variables reference different objects in memory
list1 == list2 // true: the objects they reference are equal
list1 !== list2 // true: negation of first comparison
list1 != list2 // false: negation of second comparison

Structural equality checks use the equals method that every object has for comparison. In fact,
the default implementation of a == b is a?.equals(b) ?: (b === null). So if a is not
null, it must be equal to b, and if a is null, then b must also be null.

Note that a == null is automatically translated to a === null so there’s no need for you to
optimize your code when comparing against null.

Floating Point Equality


Kotlin adheres to the IEEE Standard for Floating Point Arithmetic9 when comparing floating-
point numbers, but only if the types of both compared numbers can be statically evaluated to
Float or Double (or their nullable counterparts). Otherwise, Kotlin falls back to the Java imple-
mentations of equals and compare for Float and Double, which do not adhere to the standard
because NaN (Not-a-Number) is considered equal to itself, and -0.0f is considered less than 0.0f.
Listing 2.36 makes this corner case more tangible.

Listing 2.36 Equality of Floating Point Values


val negativeZero = -0.0f // Statically inferred type: Float
val positiveZero = 0.0f // Statically inferred type: Float
Float.NaN == Float.NaN // false (IEEE standard)
negativeZero == positiveZero // true (IEEE standard)

9. https://ieeexplore.ieee.org/document/4610935/
Exception Handling 33

val nan: Any = Float.NaN // Explicit type: Any => not Float or Double
val negativeZeroAny: Any = -0.0f // Explicit type: Any
val positiveZeroAny: Any = 0.0f // Explicit type: Any

nan == nan // true (not IEEE standard)


negativeZeroAny == positiveZeroAny // false (not IEEE standard)

These examples give the same results for Double values. Remember that, in general, you shouldn’t
compare floating-point values for equality but rather compare with tolerance due to machine
precision limitations when storing such values.

Exception Handling
Exceptions provide a standardized way to report errors in languages like Kotlin, Java, or C# so that
all developers follow the same concept of reporting and handling errors. This way, exceptions
contribute to developer productivity while also increasing robustness of the code. In this section,
you’ll learn about the principles of exception handling, how it’s done in Kotlin, how checked and
unchecked exceptions differ, and why Kotlin only has unchecked exceptions.

Principles of Exception Handling


Not only do exceptions allow you to denote erroneous or unexpected behavior or results in
your code, they also remove the obligation to handle such unexpected behavior right where it
happens. Instead, exceptions can bubble up in your code. Through exception bubbling, you’re
able to handle exceptions at the level where it’s appropriate in your code and where you know
how to recover from the exception. For instance, a common source of exceptions is input/output
(I/O) operations at the lowest levels of an application. However, at that level, you’re unlikely to
know how to handle errors—whether to retry the operation, notify the user, skip the operation
altogether, and so on. More high-level parts of your application, on the other hand, should be
able to recover from an exception like this.

Exception Handling in Kotlin


On the syntax level, exception handling consists of an operation to throw (create) exceptions
and ways to catch (handle) them. The handling of an exception may occur far from where it was
thrown. Kotlin uses the throw keyword to throw a new exception and try-catch blocks to
handle them. Listing 2.37 shows an introductory example.

Listing 2.37 Throwing and Catching Exceptions


fun reducePressureBy(bar: Int) {
// …
throw IllegalArgumentException("Invalid pressure reduction by $bar")
}
34 Chapter 2 Diving into Kotlin

try {
reducePressureBy(30)
} catch(e: IllegalArgumentException) {
// Handle exception here
} catch(e: IllegalStateException) {
// You can handle multiple possible exceptions
} finally {
// This code will always execute at the end of the block
}

The throw keyword followed by the exception class throws a new exception. Note that there’s
no new keyword for object instantiation in Kotlin. The try block contains the code that may
throw an exception. If an exception occurs, it is handled in the corresponding catch block. The
finally block guarantees that its code is executed at the end of the try-catch, irrespective of
whether an exception occurred. It is commonly used to close any open connections to resources.
Both catch and finally are optional, but one of them must be present.

Consistent with the rest of the language, try-catch blocks are expressions in Kotlin and thus
have a value. This value is again defined by the last lines inside the try- and catch blocks for
each case, as demonstrated in Listing 2.38.

Listing 2.38 Using try-catch as an Expression


val input: Int? = try {
inputString.toInt() // Tries to parse input to an integer
} catch(e: NumberFormatException) {
null // Returns null if input could not be parsed to integer
}

Here, if the input string can be parsed to an integer, its value is stored in input. Otherwise, toInt
will cause a number format exception, making the execution jump into the catch block and
resulting in the value null for the try expression. If appropriate, you could instead return a
default value inside the catch block to avoid nullability.

Taking the concept further, even throw is an expression in Kotlin. You might wonder what the
value of such an expression should be, considering that execution cannot continue to the lines
following a throw statement. But this is exactly the point because non-termination (and based
on this unreachable code) is marked by the special type Nothing in Kotlin. If you’re familiar with
domain theory, this is the bottom element. It’s also the bottom of Kotlin’s type system, meaning
it is the subtype of every other type. It has no possible values, and for Nothing? the only valid
value is null. Because throw is an expression, you can use it with the elvis operator as in
Listing 2.39 (remember you can think of the elvis operator as “or else”).
Exception Handling 35

Listing 2.39 Using throw as an Expression


val bitmap = card.bitmap ?: throw IllegalArgumentException("Bitmap required")
bitmap.prepareToDraw() // Known to have type Bitmap here (non-nullable)

Notice that the compiler makes use of the information that the Nothing type carries—namely, if
the code reaches the throw expression, the first line of code will not terminate. In other words,
if card.bitmap is null, the first line of code won’t terminate. With this, the compiler can infer
that the bitmap variable must be non-null in the second line. Otherwise, execution wouldn’t
even reach the second line.

You can also use the Nothing type to mark functions that never return to make use of this
compiler behavior. A common example is a fail function as known from testing frameworks, as
illustrated in Listing 2.40.

Listing 2.40 Using Nothing for Non-Returning Functions


fun fail(message: String): Nothing { // Nothing type => function never terminates
throw IllegalArgumentException(message)
}

val bitmap = card.bitmap ?: fail("Bitmap required") // Inferred type: Bitmap


bitmap.prepareToDraw()

Checked and Unchecked Exceptions


An unchecked exception is an exception that a developer may handle but is not forced by the
compiler to handle. In other words, the compiler doesn’t check if the exception is handled. If an
unchecked exception occurs and isn’t handled until it bubbles up to the top level (in the stack
frame), it will cause the system to crash and will show the error that caused it. In contrast, for a
checked exception, the compiler forces developers to handle it. So if a method indicates in its
signature that it may throw an exception, you have to handle that exception when calling the
method—either using a catch block or by explicitly passing along the exception. The problem
that checked exceptions as used in Java have shown over the years is that developers are distracted
by the obligation to handle exceptions, when really they want to focus on the actual logic. The
result is swallowed exceptions and empty catch blocks. These are worse than letting the excep-
tion bubble up because it just defers the error to far-away parts of the code. This makes finding
the root cause of the error exponentially harder. Plus, such try-catch blocks add unnecessary
boilerplate and complexity to the code.

There has been a lot of discussion about the merits and downsides of checked exceptions. Modern
Java-inspired languages like Kotlin, C#,10 and Ruby11 decided not to add them and instead use
only unchecked exceptions. Thus, all exceptions in Kotlin are unchecked, leaving it to the devel-
oper to decide whether to handle them. In Kotlin, this decision is also based on JetBrains’ desire
for a language for large-scale systems, and although checked exceptions have proven useful in

10. https://docs.microsoft.com/en-us/dotnet/csharp/
11. https://www.ruby-lang.org/en/
36 Chapter 2 Diving into Kotlin

smaller projects, they introduce considerable obstacles for larger projects. The reason is that, in
smaller projects, exceptions can be handled rather close to the source of the exception, whereas
large systems require developers to annotate all possible exceptions that can occur at each level
in between. That is, every function that passes along an exception and doesn’t handle it yet must
include a throws annotation in its signature. Interoperability issues associated with this differ-
ence are discussed in Chapter 5.

Java Note
Java uses both checked and unchecked exceptions, so you could use only unchecked exceptions
in your code. However, third-party code using checked exceptions forces you to handle them in
your code and add throws annotations to each function that passes along the exception.

Summary
You are now familiar with the basics of Kotlin, including data types, control flow, function
declarations, handling nullability, and exceptions. Throughout the chapter, Kotlin’s focus on
conciseness and safety became apparent with features such as type inference, explicit nullable
types, default values, function shorthand syntax, and the fact that almost all language constructs
are expressions. In addition to this, you saw how extension functions can be used to enhance the
API of classes that you don’t own, and you explored why Kotlin uses only unchecked exceptions
for exception handling.

In the following two chapters, you will explore the two main programming paradigms on which
Kotlin is based—namely, functional programming and object orientation—and how they are
incorporated into the language.
3
Functional Programming
in Kotlin

Divide each difficulty into as many parts as


is feasible and necessary to resolve it.
René Descartes

This chapter covers the fundamentals of functional programming and explains how these
conventions are incorporated into Kotlin. It starts with an overview of the principles and benefits
of functional programming and moves on to specific topics, such as higher-order functions,
lambda expressions, and lazy evaluation, which are essential in functional programming.

Purpose of Functional Programming


As the name implies, functional programming emphasizes the use of functions for application
development. Similar to object orientation, where the use of classes and objects is emphasized,
functional programming can be seen as a programming paradigm. At an abstract level, every
program or component can be seen as a function with an input and an output. This model allows
for new ways to compose and therefore modularize programs as functions. The main concepts
to understand in functional programming are higher-order functions, lambda expressions, and lazy
evaluation.
 Higher-order functions are functions that take in one or more other functions as input,
return a function as their value, or both. They may also take in other types of arguments.
The use cases for this are wide and powerful, and you will see many of them in this chapter.
 Lambda expressions are unnamed functions and typically defined on the fly. They allow
denoting functions concisely at the use site. Lambda expressions are frequently used in
combination with higher-order functions to define the functions passed in as arguments or
returned from a higher-order function.
38 Chapter 3 Functional Programming in Kotlin

 Lazy evaluation is all about evaluating expressions only as far as necessary. For instance,
when you have a sequence of a million elements, you might not want Kotlin to evaluate
all these elements at runtime unless they are actually used. This is what Kotlin’s sequences
do. Lazy sequences allow you to use infinite data structures and significantly improve
performance in certain cases.

These three major concepts of functional programming open up completely new ways to
approach and solve problems, to write highly expressive code, and to modularize code.

Apart from being a paradigm, functional programming is also a programming style in which you
make heavy use of immutability and avoid functions with side effects. Immutability increases
robustness of the code, especially in multithreaded environments, because it avoids unwanted
state changes. Avoiding such side effects of functions improves understandability, testability, and
robustness because it minimizes hidden and unexpected state changes. This also helps reason
about lazily evaluated expressions because these may otherwise work with a mutated state.

Also, pure functional languages normally do not offer null, as opposed to object-oriented
languages. This maps well to Kotlin’s ideals, which advocate immutability and avoiding the use
of null, which is typically used in combination with mutable objects. However, as an object-
oriented language that must interoperate with Java, Kotlin does still have null.

Java Note
Kotlin’s lazy sequences are conceptually the same as Java 8 Streams,1 and their usage is very
similar as well.

Benefits of Functional Programming


There are many ways in which functional programming can improve your code, and it works well
in combination with object orientation.

First, higher-order functions and lambda expressions can greatly improve the conciseness and
expressiveness of code. For instance, they often allow replacing imperative-style loops with a
single line of code. Also, they prove useful for working with collections such as lists and sets—
again stemming from the fact that you can use more concise alternatives to imperative code.

Second, lazy evaluation can significantly enhance performance when working with large collec-
tions or when performing expensive computations on the items of a collection. Also, lazy
sequences in Kotlin allow for the definition of infinite data types, such as a list of all prime
numbers. Internally, this means that any item in the list will be computed on the fly and only
once it becomes necessary, similar to invoking a function to compute the first n primes for some
number n.

1. https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html
Functions 39

Third, functional programming opens up new ways to solve common development tasks. This
includes working with collections and implementing design patterns such as the Strategy pattern2
more easily using lambda expressions.

You will be able to form your own picture of the benefits of functional programming when going
through this chapter and applying the concepts in your code. You should use the presented
features with discipline. Not everything has to be solved in a purely functional style, especially
because it may be unfamiliar to other developers reading the code. But used appropriately in
combination with Kotlin’s other language features, it allows you to write cleaner, more readable
code and synergizes with other paradigms such as object orientation.

Functions
Naturally, functions are at the heart of functional programming. This section introduces the
concepts of a function signature and function types, and it demonstrates that Kotlin supports
proper function types.

At its core, a function is a modularized block of code that takes in zero or more input arguments,
performs some computations on them, and may return a result value. Information about parame-
ters and return values is encapsulated in the function signature. Listing 3.1 demonstrates a function
signature in Kotlin. It defines the function’s name, its inputs, and its outputs (in that order).

Listing 3.1 Function Signature


fun countOccurrences(of: Char, inStr: String): Int // Inputs: Char, String; Out: Int

Because functions are first-class members in Kotlin, you can denote the type of a function using
proper function types, for instance, as the type of a variable holding a function. This is shown in
Listing 3.2.

Listing 3.2 Function Type


val count: (Char, String) -> Int = ::countOccurrences // Function ref. matches type

The count variable holds a function, and its type is (Char, String) -> Int, meaning a func-
tion with a Char as first parameter, a String as second parameter, and an Int return value. We
call this a first-order function—in contrast to higher-order functions—because it has no functions
as parameters or return types.

In order to refer to a named function, you can use function pointers by prefixing the function
name with two colons, as in ::countOccurrences. As you will see later, you use lambda expres-
sions instead in many cases because they are more flexible.

2. https://sourcemaking.com/design_patterns/strategy
40 Chapter 3 Functional Programming in Kotlin

Java Note
There are no proper function types in Java 8. Instead, there is only a fixed number of
predefined function types.3

Lambda Expressions
Lambda expressions allow the definition of unnamed functions at the call site, without the boil-
erplate of declaring a named function. Oftentimes, we will refer to lambda expressions as lambdas
for short.

Lambdas are one of the main features of most modern programming languages like Scala, Java 8,
or Dart. Since it intends to be a conglomerate of best practices, it is no wonder that Kotlin also has
lambda expressions baked into the language. Because Kotlin supported lambdas right from the
start and doesn’t carry much legacy, it can provide smooth syntactic support for them.

Using Lambdas in Kotlin


Kotlin has powerful syntactic support for lambdas, allowing a very concise and readable notation.
In its full glory, a lambda expression can be written in Kotlin as shown in Listing 3.3.

Listing 3.3 Full Lambda Expression


{ x: Int, y: Int -> x + y } // Lambdas are denoted with bold braces in this chapter

Lambda expressions are wrapped in curly braces, their parameters defined on the left-hand side
of the arrow, and the function body on the right-hand side. This lambda function takes in two
integers and returns their sum, so its type is (Int, Int) -> Int. Hence, you could assign it to a
variable of that type, as done in Listings 3.4 through 3.6.

You can use Kotlin’s type inference when working with lambdas, as well as some other syntactic
sugar. Let us start with the lambda expression from Listing 3.3, assign it to a variable, and improve
the code step by step. Listing 3.4 shows the most explicit way to assign it to a variable, with all
types mentioned.

Listing 3.4 Assigning a Lambda Expression


val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }

First of all, Kotlin can infer from the variable type that x and y must be of type Int, so you don’t
have to repeat it inside the lambda. This is shown in Listing 3.5.

3. https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
Higher-Order Functions 41

Listing 3.5 Type Inference for Lambda Arguments


val sum: (Int, Int) -> Int = { x, y -> x + y } // Infers the lambda parameter types

Conversely, you can use explicit types inside the lambda and then let Kotlin infer the variable
type, as in Listing 3.6.

Listing 3.6 Type Inference for Lambda Variables


val sum = { x: Int, y: Int -> x + y } // Infers the variable type

Kotlin not only infers the parameter types but also the return type from the lambda’s definition.

If a lambda expression has only one argument, there is another way to write lambdas concisely.
To see this, consider a lambda that transforms a String to uppercase so that the string is the only
argument. You can then refer to the string via the implicit parameter name it and then skip the
parameter list completely, as shown in Listing 3.7.

Listing 3.7 Implicit Argument Name it


val toUpper: (String) -> String = { it.toUpperCase() } // Uses the implicit ‘it’

Note that this only works if Kotlin can infer the type of it, which is usually the case because you
use lambdas primarily as arguments for higher-order functions.

In this particular case, you can use a function reference instead of a lambda because you are just
applying String.toUpperCase and this is actually a predefined, named function. Thus, Listing 3.8
is equivalent to Listing 3.7.

Listing 3.8 Function Pointers


val toUpper: (String) -> String = String::toUpperCase // Uses function reference

Lambda expressions are used to avoid the overhead of defining named functions whenever it is
not necessary, for instance, when they are only used in one specific place. If there already is a
named function that can be used directly instead, there is no need to wrap its call into a lambda
expression; you can reference the named function instead.

Higher-Order Functions
Higher-order functions take another function as input or return a function. Consider the defini-
tion of a higher-order function called twice shown in Listing 3.9.
42 Chapter 3 Functional Programming in Kotlin

Listing 3.9 Defining a Higher-Order Function


fun twice(f: (Int) -> Int): (Int) -> Int = { x -> f(f(x)) } // Applies ‘f’ twice

This function takes in a function f that maps an Int to an Int and also returns a function from
Int to Int. More specifically, it returns the function that applies f twice.

Higher-order functions unfold their full potential only in combination with lambda expressions
to pass in the arguments. For example, to call the higher-order function twice, you must pass in
a function for f. You can do this using a lambda expression or a function reference. Both options
are shown in Listing 3.10.

Listing 3.10 Calling a Higher-Order Function


val plusTwo = twice({ it + 1 }) // Uses lambda
val plusTwo = twice(Int::inc) // Uses function reference

When using a lambda, Kotlin infers the type of it using the type of f, which is (Int) -> Int.
Kotlin thus infers it to be an integer and validates your lambda based on this type information.

Whenever the last argument of a higher-order function is a lambda expression, you can move the
lambda out of the parentheses, as in Listing 3.11.

Listing 3.11 Lambdas as Last Argument


val plusTwo = twice { it + 1 } // Moves lambda out of parentheses and omits them

Why would you do this? First, for a higher-order function with a lambda as its only argument, it
allows skipping the parentheses altogether, as in Listing 3.11. You will see many such functions
when discussing collections in the next section. Second, this lets you define higher-order func-
tions that look like language keywords and encapsulate related code into blocks. This will become
clearer once we explore the Kotlin standard library at the end of this chapter. Third, this feature
is what allows building simple domain-specific languages (DSLs) in Kotlin. This is covered in
Chapter 9, Kotlin DSLs.

The Kotlin standard library includes many useful higher-order functions that facilitate certain
development tasks. You will get to know some of them later in this chapter when exploring
collections and Kotlin’s scoping functions.

Inlining Higher-Order Functions


When working with higher-order functions, it is important to consider what happens at runtime.
Functions are derived from classes and represented as objects at runtime. This has important
performance implications. Consider Listing 3.12’s call to the twice function from Listing 3.9.
Higher-Order Functions 43

Listing 3.12 Call to Higher-Order Function


val plusTwo = twice { it + 1 } // Creates a function object

At runtime, the lambda expression becomes an object. More generally, for every lambda that you
use, an object must be created at runtime—leading to memory consumption and garbage-collector
invocations that could be avoided.

Higher-order functions can be declared as inline functions in order to avoid this problem. For
instance, consider the forEach function from Kotlin’s standard library, shown in Listing 3.13.
Note that forEach is a generic function, recognizable by its generic type parameter <T>. Generics
are discussed in detail in Chapter 4, Object Orientation in Kotlin.

Listing 3.13 Inlining Higher-Order Functions


public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit { // Uses inline
for (element in this) action(element)
}

(1..10).forEach { println(it) } // This function call is inlined at compile-time

To declare an inline function, you add the inline modifier to the function declaration. This way,
lambda expressions passed to the higher-order function are inlined at compile time. Intuitively,
the code will be copy-and-pasted to the call site so that the last line from Listing 3.13 would result
in the code from Listing 3.14 getting compiled.

Listing 3.14 Result of Inlined Function Call


for (element in 1..10) println(element) // No lambda, thus no object at runtime

Inline functions are used frequently in the Kotlin standard library because there is often no need
for function objects at runtime. If you do need a function object at runtime, for instance to pass
it to another higher-order function, you cannot use inlining. Also, note that inlining only makes
sense for higher-order functions, and only for those that accept another function as an argument
because this is where inlining prevents objects at runtime. If you use inline where it’s useless,
the Kotlin compiler will give you a warning.

If you have multiple functions as parameters but only want to inline some of them, you can
exclude the others by placing the noinline modifier in front of the parameter name.

Java Note
Although the Java compiler may decide to inline function calls, there is no way to specify that a
certain higher-order function should always inline its arguments.
44 Chapter 3 Functional Programming in Kotlin

Non-Local Returns
Lambda expressions in Kotlin are not allowed to return from their enclosing function. Thus,
you cannot use a bare return inside a lambda—these can only be used to return from functions
declared using fun. However, if the lambda’s enclosing function is inlined, then it is possible to
use return because the return is inlined as well. Listing 3.15 shows a typical example using the
higher-order function forEach.

Listing 3.15 Returning from an Enclosing Function


fun contains(range: IntRange, number: Int): Boolean {
range.forEach {
if (it == number) return true // Can use return because lambda will be inlined
}
return false
}

Here, the lambda passed to forEach returns from the enclosing contains function. This is
called a non-local return because the return is inside the lambda, but it returns from the enclosing
scope. It is allowed because forEach is an inline function so that the code from Listing 3.15 effec-
tively becomes the code shown in Listing 3.16.

Listing 3.16 Returning from an Enclosing Function—Inlined Result


fun contains(range: IntRange, number: Int): Boolean {
for (element in range) {
if (element == number) return true // This return is equivalent to the one above
}
return false
}

This shows that the return inside the lambda ends up as a regular return inside a loop. Thus, it
is possible to allow returns inside the lambda in this case. In general, it is possible to do so if the
higher-order function calls the lambda directly inside its function body.

In other cases, where the lambda is called from a different execution context such as another
lambda or from inside an object, this is not possible because it would try to return from that
other execution context instead of from the enclosing higher-order function. Thus, if you want to
inline such a higher-order function, you must disallow returns from the lambda. This is what the
crossinline modifier is for.

For instance, to inline the twice function from Listing 3.9, you must disallow returns from its
lambda named f because twice calls f from inside another lambda. A valid inlined definition of
twice is shown in Listing 3.17.
Working with Collections 45

Listing 3.17 Disallowing Non-Local Returns Using crossinline


inline fun twice(crossinline f: (Int) -> Int): (Int) -> Int = { x -> f(f(x)) }

Non-local returns from inside f must be disallowed here because they would be located inside
their wrapping lambda expression after inlining—and as mentioned above, lambdas cannot
normally use return. Additionally, the inner f would even try to return from inside its wrapping
invocation of f, which is also not allowed.

Working with Collections


Kotlin collections are based on the Java Collections API, but there are some important differences
in their declaration and also their use. This section covers how to work with collections and then
explores higher-order functions defined on collections that greatly facilitate working with them.

Kotlin Collections API versus Java Collections API


Consistent with Kotlin’s focus on immutability, the Kotlin Collections API clearly differentiates
between mutable and read-only collections. For each collection type (lists, sets, and maps), there
is a read-only and a mutable collection type in Kotlin, namely List and MutableList, Set and
MutableSet, and Map and MutableMap. It is thus important to remember that the collection
types without “mutable” in their names are read-only in Kotlin and should be the first choice
unless you really need a mutable collection.

Once initialized, you cannot add, replace, or remove elements from a read-only collection.
However, you can still mutate any mutable objects stored inside the collection. So to achieve
immutability with collections, you now have three levels to consider.
 Using val to hold the collection, meaning that you cannot reassign the variable
 Using an immutable collection (or at least a read-only one)
 Storing immutable objects inside the collection

Note
Kotlin’s collections are not immutable in a strict sense because there are (hacky) ways to
mutate them. This is why we refer to them as read-only in this book. However, the Kotlin team
is working on truly immutable collections as well.4

Each of the mutable subinterfaces MutableList, MutableSet, and MutableMap add mutating
methods such as add, set, or put to the interface definition. Thus, you can add, replace, and
remove elements from these mutable collections.

4. https://github.com/Kotlin/kotlinx.collections.immutable
46 Chapter 3 Functional Programming in Kotlin

As you will see in the following section, the distinction between read-only and mutable collec-
tions is also reflected in the way you instantiate collections in Kotlin.

Instantiating Collections in Kotlin


The Kotlin collection types are typically instantiated using one of the available helper functions
that allow you to pass in an arbitrary number of items (a so-called vararg), as in Listing 3.18.

Listing 3.18 Helper Functions for Creating Collections


// Collections
val languages = setOf("Kotlin", "Java", "C++") // Creates a read-only set
val votes = listOf(true, false, false, true) // Creates a read-only list
val countryToCapital = mapOf( // Creates a read-only map
"Germany" to "Berlin", "France" to "Paris")

// Arrays
val testData = arrayOf(0, 1, 5, 9, 10) // Creates an array (mutable)

This way, you can instantiate sets, lists, maps, and other collection types easily. All of the functions
mentioned above create read-only collections, apart from the arrayOf function that was included
because arrays are similar to collections, although they do not fall under the Collections API.

Apart from the mentioned helper functions, there are also ones to create mutable collections and
collections of specific subtypes, as portrayed in Table 3.1.

Table 3.1 Helper Functions to Create Collections


Result Type List Set Map
Read-Only Collection listOf setOf mapOf
Mutable Collection mutableListOf mutableSetOf mutableMapOf
Specific Subtype arrayListOf hashSetOf hashMapOf

linkedSetOf linkedMapOf

sortedSetOf sortedMapOf

For arrays, there are also helper functions for all of Java’s primitive types such as intArrayOf,
doubleArrayOf, and booleanArrayOf. In the bytecode, these translate to arrays of the corre-
sponding primitive type in Java such as int[], double[], and boolean[] instead of Integer[],
Double[], and Boolean[], respectively. This is important in performance-critical code to avoid
unnecessary object creation, autoboxing, unboxing, and memory consumption.
Working with Collections 47

Accessing and Editing Collections


The way you access and edit a collection depends on the type of the collection and whether it is
mutable or read-only. The method names of Java’s collections have mostly been kept the same
in Kotlin. But whenever a get method is defined, it is possible—and recommended—to use the
indexed access operator, which is shown in Listing 3.19.

Listing 3.19 Indexed Access Operator for Reading


votes[1] // false
testData[2] // 5
countryToCapital["Germany"] // "Berlin"

There are several things to notice here. First, the example excludes the languages set because
sets, per definition, have no concept of ordering. So it is not possible to select an n-th element
from a set. Next, the Map type defines a get method, which takes in a key, in this case a String.
Internally, the indexed access operator simply calls the get method on the object. You could do
this explicitly with the same result, for instance, calling votes.get(1).

For mutable collections that define a set method, you can also use the indexed access operator to
replace elements. For Listing 3.20, assume you had created mutable collections above so that you
can replace their elements.

Listing 3.20 Indexed Access Operator for Writing


// Assumes mutable collections now
votes[1] = true // Replaces second element with true
testData[2] = 4 // Replaces third element with 4
countryToCapital["Germany"] = "Bonn" // Replaces value for key "Germany"

This internally calls the set method defined for each of these mutable collection types, for
instance, votes.set(1, true).

With the possibility to instantiate and manipulate their elements, you can now look at how to
work with collections effectively using common higher-order functions.

Filtering Collections
A common task is to filter a collection of elements based on some condition. In Kotlin, you can use
the higher-order function filter for this task. It is a higher-order function because the predicate
is passed in as a function. Imagine you want to count the number of yes votes as in Listing 3.21.

Listing 3.21 Filtering a Collection


val numberOfYesVotes = votes.filter { it == true }.count() // Counts positive votes
48 Chapter 3 Functional Programming in Kotlin

Because the lambda is the filter’s only argument, you can skip the parentheses altogether. For a
collection of elements of type A, the predicate must be a function of type (A) -> Boolean. This
can also be used for search. For instance, search countries starting with an “f” as done in Listing 3.22.

Listing 3.22 Searching a Collection


val searchResult = countryToCapital.keys.filter { it.toUpperCase().startsWith('F') }

Essentially, the filter function filters a collection to one of its subsets where only the elements
fulfilling the given predicate remain.

Mapping Collections
The higher-order function map is also well known from functional languages. It allows you to
easily apply (or “map”) a given function to each element of a collection. Imagine you want to
square each element in the array testData as in Listing 3.23.

Listing 3.23 Applying a Function to Each Element Using map


val squared = testData.map { it * it } // Creates list with each element squared

Again, you can omit the parentheses for the call to the map function because the lambda is the
only argument. For a given element, the lambda returns the element squared (note that it has
type Int here). Since this operation will be performed on each element, the squared variable will
be a list with the elements 0, 1, 25, 81, 100. Notice that map always returns a list, even if you begin
with a different collection type. You can see this looking at its signature given in Listing 3.24.

Listing 3.24 Signature of the map Function


inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> // Returns List

The map function can also perform a projection (similar to SQL5), where a collection of objects is
mapped to a collection of only relevant attributes, as shown in Listing 3.25.

Listing 3.25 Projections Using map


val countries = countryToCapital.map { it.key } // ["Germany", "France"]

Here, each element in the collection has the type Entry<String, String>, so this is the type
of it in this example. Each of those entries consists of a key and a value, which is projected to
only the key, which contains the country name.

5. http://www.cs.uakron.edu/~echeng/db/sequel.pdf
Working with Collections 49

Grouping Collections
You can group a collection’s elements by some key using the higher-order function groupBy. For
instance, you could group people by their first name, users by their status, or animals by their age.
Listing 3.26 gives an example that groups words by their length.

Listing 3.26 Grouping Words by Length


val sentence = "This is an example sentence with several words in it"
val lengthToWords = sentence.split(" ") // ["This", "is", "an", …, "in", "it"]
.groupBy { it.length }

println(lengthToWords) // {4=[This, with], 2=[is, an, in, it], … }

First, split turns the sentence into a list of its words by splitting the string at every space, and
then the groupBy function groups the words by their length. In general, groupBy transforms any
Iterable into a map, and the keys for that map are defined by the lambda you pass to groupBy.
Here, the keys are the word lengths because the lambda uses it.length as the key.

Associating Collections
Another higher-order function that turns an iterable object into a map is associate. This one
lets you associate two different parts of your data to each other. For instance, you could associate
your users’ ages to their average shopping cart value or your users’ subscription plans to the time
they spend on your site. Another use case is simply transforming a list into a map. Listing 3.27
does this and uses incrementing integers as the map’s keys.

Listing 3.27 Turning a List into a Map


val words = listOf("filter", "map", "sorted", "groupBy", "associate")
var id = 0 // The first map key
val map = words.associate { id++ to it } // Associates incrementing keys to elements
println(map) // {0=filter, 1=map, 2=sorted, 3=groupBy, 4=associate}

Inside the lambda, you define the pairs that make up the resulting map. Here, each word is
mapped to a pair with the next ID as its key and the word as its value.

Calculating a Minimum, Maximum, and Sum


If you have a list of integers named numbers, you can simply use numbers.min(), numbers.
max(), and numbers.sum() in Kotlin to calculate their minimum, maximum, and sum, respec-
tively. But what if you have a list of users and want to get the one with minimum age, get the one
with maximum score, or calculate the sum of their monthly payments? For these cases, Kotlin has
50 Chapter 3 Functional Programming in Kotlin

higher-order functions called minBy, maxBy, and sumBy. Listing 3.28 assumes you have users with
an age, score, and monthlyFee to implement these use cases.

Listing 3.28 Minimum, Maximum, and Sum by Some Metric


users.minBy { it.age } // Returns user object of youngest user
users.maxBy { it.score } // Returns user object of user with maximum score
users.sumBy { it.monthlyFee } // Returns sum of all users’ monthly fees

As you can see, these allow for a readable and terse syntax for such use cases. Note that minBy
{ it.age } is not equivalent to map { it.age }.min() because the latter doesn’t return the
whole user but just the minimum age that was found.

Sorting Collections
Kotlin provides several functions to sort collections, some of which accept a function as a param-
eter. They are shown in Listing 3.29.

Listing 3.29 Sorting Collections


languages.sorted() // ["C++", "Java", "Kotlin"]
languages.sortedDescending() // ["Kotlin", "Java", "C++"]
testData.sortedBy { it % 3 } // [0, 9, 1, 10, 5]
votes.sortedWith(
Comparator { o1, _ -> if (o1 == true) -1 else 1 }) // [true, true, false, false]
countryToCapital.toSortedMap() // {France=Paris, Germany=Berlin}

All of the methods used above do not change the original object on which they are called. In
other words, they do not perform the sort in place but create a new collection and return that
instead. For lists, sets, and arrays, these sorting methods all return a list. For arrays, there is also
sortedArray that returns an array. For maps, as in the last line, there is only toSortedMap,
which returns a SortedMap—a map that is sorted by the natural ordering on its keys by default
(but can also accept a Comparator).

The default sorting operations in the first two lines use natural ordering, in this case the lexico-
graphical ordering on strings.

For more special use cases, the method sortedBy allows the developer to define a custom value
by which to sort. Listing 3.29 sorts the elements in testData by their remainder when dividing
by three. In general, the function you pass in must take in an element of the collection and return
some subtype of Comparable; here (Int) -> Int is used.

Lastly, sortedWith accepts a java.util.Comparator that defines how two elements ought to
be compared. The way this works is that you compare two objects and return a negative number if
Working with Collections 51

the first is less than the second and vice versa. Here, affirmative votes are defined to be “less than”
negative votes, thus sorting them to the beginning. Notice that, since you do not need the second
vote to compute the result, you can use an underscore in place of the lambda argument to signify
that the second argument is not used.

For arrays, there are also in-place sorting operations following the same structure of method
names but prefixed with sort instead of sorted, for instance sortDescending and sortBy.

Folding Collections
The fold function is used to transform a collection into a value by iteratively applying a function
to two adjacent elements at a time, starting with a given initial value and the first element in the
collection, then progressing through the collection from the “left” (the start).

To make this more tangible, consider the implementations of calculating the sum and product of
a collection shown in Listing 3.30.

Listing 3.30 Calculating Sum and Product Using fold


val testData = arrayOf(0, 1, 5, 9, 10)
val sum = testData.fold(0) { acc, element -> acc + element } // 25
val product = testData.fold(1) { acc, element -> acc * element } // 0

In these examples, the type of the accumulator is the same as the element type—both are inte-
gers. In general, any type of accumulator can be used. The first line will result in the computation
of (((((0 + 0) + 1) + 5) + 9) + 10). The name fold comes from the fact that you can imagine this
operation to fold the collection element-wise, each time applying the given function to the hith-
erto accumulated value and the next element.

First, the additional zero start value is folded onto the first collection element, resulting in the
computation of (0 + 0) = 0. This new value is folded onto the second element, thus calculating
(0 + 1) = 1. Next comes (1 + 5) = 6, and so forth. A common way to visualize this behavior is shown
in Figure 3.1, where the colon stands for appending an element, and [] represents an empty array.

: +

0 : fold(0) { i, j –> i + j } + 10

1 : + 9

5 : + 5

9 : + 1

10 [] 0 0

Figure 3.1 Visualization of the fold function


52 Chapter 3 Functional Programming in Kotlin

The left-hand side of the diagram represents the testData array with elements 0, 1, 5, 9, and 10.
The right-hand side shows the computation tree that fold goes through, from bottom to top.
Because this operation folds the collection from left to right, it is also known as left fold. Kotlin
also provides a function foldRight, which works similarly but folds the collection from right to
left. For the two examples above, both operations deliver the same result because both addition
and multiplication are commutative operations: (((((0 + 0) + 1) + 5) + 9) + 10) is equal to (0 +
(0 + (1 + (5 + (9 + 10))))).

In abstract terms, the signature of the fold function on an Iterable<E> can be denoted as
(R, (R, E) -> R, Iterable<E>) -> R where E is the type of element stored in the collec-
tion and R is the accumulator type of fold. The first parameter is a start value, which is combined
with the first collection element for the first fold step. The second parameter is a function that
defines the next value based on the accumulated value so far (of type R) and the next collection
element of type E, returning the next accumulator value. The last parameter is the iterable that
fold is called on, and ultimately it returns the accumulated value of type R.

Note
Kotlin also offers the functions reduce and reduceRight, which are very similar to fold and
foldRight but work without an additional start value; they begin by folding the first (or last)
two collection elements onto each other, respectively.
For the examples above, reduce is actually preferable because adding an additional zero or
multiplying by one are redundant. However, to have a constant offset, for instance, the folding
start value can be used.

Chaining Function Calls


In actual applications that manipulate collections, it is common to see chains of calls to higher-
order functions, effectively forming a pipeline of computations. This is possible because these
functions again return their modified collection so that the following function can work with it.
Consider having a long list of User objects stored in a users variable, and you want to print the
user names of the first ten active users, as shown in Listing 3.31.

Listing 3.31 Chaining Functions


val activeUserNames = users.filter { it.isActive } // Filter active users
.take(10) // Take first ten users
.map { it.username } // Extract their usernames

The implicit it variable and the possibility of chaining the function calls allows you to write
extremely concise code. Note that take returns the first n elements of an Iterable. We will
discuss this in more detail in the section on lazy sequences.
Scoping Functions 53

In the Kotlin standard library, there are more useful higher-order functions defined that facilitate
working with collections and can be used in a similar way. In the section about lazy sequences, we
will discuss performance concerns for chains of function calls.

Scoping Functions
This section introduces Kotlin’s scoping functions from the file Standard.kt in the standard
library. It also covers the use function defined in Closeable.kt because it is similar to the
scoping functions. All these functions help avoid common sources of errors, structure your code,
and avoid code duplication.

Using let
The let function is a useful tool for variable scoping and for working with nullable types. It has
three main use cases.
 Scoping variables so that they are only visible inside a lambda
 Executing some code only if a nullable variable is not null
 Converting one nullable object into another

First, variable scoping can be used whenever a variable or object should only be used in a small
region of the code, for instance, a buffered reader reading from a file, as shown in Listing 3.32.

Listing 3.32 Scoping with let


val lines = File("rawdata.csv").bufferedReader().let { // Scopes the buffered reader
val result = it.readLines() // Accesses reader as ‘it’
it.close()
result
}
// Buffered reader and ‘result’ variable are not visible here

This way, the buffered reader object and all variables declared inside the let block (the lambda
expression) are only visible inside that block. This avoids unwanted access to the reader from
outside this block. Notice that, similar to if and when expressions, let returns the value defined
in the last line of the lambda. This example returns a list of all lines in a file and must store that
into a temporary variable in order to call close in between and close the file—we will improve
this later with use.

Second, let is useful to work with nullable types because, when called on a nullable object
using the safe call operator, the let block will only be executed if that object is not null.
Otherwise, the let block will simply be ignored. Imagine trying to fetch weather data with a
network call that may return null, as illustrated in Listing 3.33.
54 Chapter 3 Functional Programming in Kotlin

Listing 3.33 Handling Nullables with let


val weather: Weather? = fetchWeatherOrNull()

weather?.let {
updateUi(weather.temperature) // Only updates UI if ‘weather’ is not null
}

When using let with a safe-call operator like this, then inside the let block, the Kotlin compiler
helps you by automatically casting the weather variable to a non-nullable type so that it can be
used without further addressing nullability. If the weather data could not be fetched, the lambda
passed to let is not run. Thus, the UI will not be updated.

Lastly, you can convert one nullable object into another with the same construct as in Listing 3.33
by calling let on the original nullable object and defining the desired converted value in the last
line inside the let block. This last line defines the return value of let.

This is the first time you are using higher-order functions and lambda expressions in a way that
looks as if let was a keyword of the language, stemming from the syntax without parentheses
and with curly braces containing a block of code. It is important to keep in mind that these are
regular higher-order functions, and that you can look at their declaration at any time.

Using apply
The higher-order function apply has two primary use cases.

 Encapsulating multiple calls on the same object


 Initializing objects

Listing 3.34 shows how to encapsulate multiple calls on the same object into an own code block.
In the following subsections, let’s assume countryToCapital was a mutable map, not a read-
only one, so that you can add new elements to it.

Listing 3.34 Using apply


countryToCapital.apply {
putIfAbsent("India", "Delhi") // Accesses ‘countryToCapital’ methods without prefix
putIfAbsent("France", "Paris")
}

println(countryToCapital) // {"Germany"="Berlin", "France"="Paris", "India"="Delhi"}


Scoping Functions 55

With apply, you do not refer to it because the lambda you pass in is executed like an extension
function on the object that you call apply on, here countryToCapital. Thus, you can write the
code inside the apply block as if it were part of the MutableMap class. You could write
this.putIfAbsent, but using this as a prefix is optional.

Most commonly, apply is used for object initialization as in Listing 3.35.

Listing 3.35 Initializing Objects with apply


val container = Container().apply { // Initializes object inside ‘apply’
size = Dimension(1024, 800)
font = Font.decode("Arial-bold-22")
isVisible = true
}

This approach uses the fact that apply first runs any code in its lambda and then returns the
object that it was called on. Thus, the container variable includes any changes made to the
object inside the lambda. This also enables chaining apply with other calls on the object, as
shown in Listing 3.36.

Listing 3.36 Return Value of apply


countryToCapital.apply { … } // ‘apply’ returns modified ‘countryToCapital’
.filter { … } // ‘filter’ works with result of ‘apply’
.map { … } // ‘map’ works with result of ‘filter’

Using this structure, coherent blocks of code that operate on the same object are clearly high-
lighted in the code through their indentation, plus there is no need to repeat the variable name
for each call.

Using with
The with function behaves almost like apply and has two major use cases.

 To encapsulate multiple calls on the same object


 To limit the scope of a temporary object

In contrast to apply, it returns the result of the lambda, not the object on which it was called.
This is shown in Listing 3.37.
56 Chapter 3 Functional Programming in Kotlin

Listing 3.37 Using with for Multiple Calls on Same Object


val countryToCapital = mutableMapOf("Germany" to "Berlin")

val countries = with(countryToCapital) {


putIfAbsent("England", "London")
putIfAbsent("Spain", "Madrid")
keys // Defines return value of the lambda, and therefore of ‘with’
}

println(countryToCapital) // {Germany=Berlin, England=London, Spain=Madrid}


println(countries) // [Germany, England, Spain]

This shows that the return value of with is the return value of the lambda you pass in—defined
by the last line inside the lambda. Here, the expression in the last line is the keys of the map, so
the with block returns all countries stored as keys in the map.

Similar to let, you can limit the scope of an object to only the lambda expression if you create
that object ad-hoc when passing it into with. This is demonstrated in Listing 3.38.

Listing 3.38 Using with to Minimize Scope


val essay = with(StringBuilder()) { // String builder only accessible inside lambda
appendln("Intro")
appendln("Content")
appendln("Conclusion")
toString()
}

Note that with is superior to apply in this case because you don’t want to get the string builder
back as a result but rather the lambda’s result. Builders like this are the most common example,
where with is preferable to apply.

Using run
The run function is helpful to:

 Work with nullables as with let but then use this inside the lambda instead of it.
 Allow immediate function application.
 Scope variables into a lambda.
 Turn an explicit parameter into a receiver object.

First, run can be seen as a combination of with and let. Imagine that countryToCapital was
nullable, and that you want to call multiple methods on it, as shown in Listing 3.39.
Scoping Functions 57

Listing 3.39 Using run with Nullables


val countryToCapital: MutableMap<String, String>?
= mutableMapOf("Germany" to "Berlin")

val countries = countryToCapital?.run { // Runs block only if not null


putIfAbsent("Mexico", "Mexico City")
putIfAbsent("Germany", "Berlin")
keys
}

println(countryToCapital) // {Germany=Berlin, Mexico=Mexico City}


println(countries) // [Germany, Mexico]

It fulfills the same purpose as with in Listing 3.37 but skips all operations in the lambda if the
map is null.

Next, you can use run for immediate function application, which means plainly running a given
lambda. This is shown in Listing 3.40.

Listing 3.40 Using run for Immediate Function Application


run {
println("Running lambda")
val a = 11 * 13
}

This example already hints at the next use case, namely minimizing the scope of temporary vari-
ables to a lambda expression, as done in Listing 3.41.

Listing 3.41 Using run to Minimize Variable Scope


val success = run {
val username = getUsername() // Only visible inside this lambda
val password = getPassword() // Only visible inside this lambda
validate(username, password)
}

Here, username and password are only accessible where they really have to be. Reducing scopes
like this is a good practice to avoid unwanted accesses.
58 Chapter 3 Functional Programming in Kotlin

Lastly, you can use run to transform an explicit parameter into a receiver object if you prefer
working with it that way. This is demonstrated in Listing 3.42, which assumes a User class that
has a username.

Listing 3.42 Using run to Turn Parameter into Receiver


fun renderUsername(user: User) = user.run { // Turns parameter into receiver
val premium = if (paid) " (Premium)" else "" // Accesses user.paid
val displayName = "$username$premium" // Accesses user.username
println(displayName)
}

Inside the lambda expression, you can access all members of the User class directly (or via this).
This is especially useful if the parameter name is long and you have to refer to it often inside the
function.

Using also
This last higher-order function from Standard.kt has two main use cases.

 Performing ancillary operations like validation or logging


 Intercepting function chains

First, consider Listing 3.43 for an example that performs validation using also.

Listing 3.43 Using also for Validation


val user = fetchUser().also {
requireNotNull(it)
require(it!!.monthlyFee > 0)
}

First, the fetchUser function is called and may return null. After that, the lambda is executed,
and only if it runs through is the variable assigned to the user. The usefulness of this function for
logging and other operations “on the side” only becomes apparent in a chain of function calls, as
in Listing 3.44.

Listing 3.44 Using also for Ancillary Operations in a Function Chain


users.filter { it.age > 21 }
.also { println("${it.size} adult users found.") } // Intercepts chain
.map { it.monthlyFee }
Scoping Functions 59

Outside of such chains, there’s no direct need to use also because you could do logging and
other side operations on the next line. In call chains, however, also lets you intercept the inter-
mediate results without breaking the chain.

Using use
The use function is not part of Standard.kt but has a similar structure and benefits. It ensures
that the resource it is called on is closed after the given operations are performed. For this reason,
it is only defined for subtypes of Closeable like Reader, Writer, or Socket. Using this, you can
improve the code from Listing 3.32 as shown in Listing 3.45.

Listing 3.45 Handling Closeables with use


val lines = File("rawdata.csv").bufferedReader().use { it.readLines() }

Since the buffered reader is now closed automatically at the end of the use block, there is no need
to store the result in a temporary variable, and you can reduce the lambda to a single line. Apart
from ensuring close is called, use is just like let. Internally, it uses a finally block to ensure
the Closeable instance is ultimately closed.

Java Note
The use function is equivalent to Java’s try-with-resources (introduced in Java 7).

Combining Higher-Order Functions


The power of higher-order functions partly stems from the possibility to chain them. You’ve
already seen several examples of this. Here, we want to give two more examples to help you famil-
iarize yourself with the functional style. First, Listing 3.46 combines Kotlin’s scoping functions to
build a SQL query using some SqlQuery class.

Listing 3.46 Combining Scope Operators


val sql = SqlQuery().apply { // ‘apply’ initializes object
append("INSERT INTO user (username, age, paid) VALUES (?, ?, ?)")
bind("johndoe")
bind(42)
bind(true)
}.also {
println("Initialized SQL query: $it") // ‘also’ intercepts computation chain
}.run {
DbConnection().execute(this) // ‘run’ applies given operations
}
60 Chapter 3 Functional Programming in Kotlin

As recommended in this section, this code uses apply to initialize the SqlQuery object, then
intercepts the chain using also to log the resulting query, and then executes the query using
run. This way, the scopes of the SqlQuery and DbConnection objects are restricted so that
they’re not accessible from outside the function chain.

Beyond this, you can combine the scope operators with the other higher-order functions as well.
Listing 3.47 gives an example of how this works with a map (that maps authors to their list of
books) to find all authors that have at least one book stored in the map.

Listing 3.47 Combining All Higher-Order Functions


val authors = authorsToBooks.apply {
putIfAbsent("Martin Fowler", listOf("Patterns of Enterprise Application Arch"))
}.filter {
it.value.isNotEmpty()
}.also {
println("Authors with books: ${it.keys}")
}.map {
it.key
}

Here, first apply is used to add another author if it doesn’t exist already, then it filters out authors
whose list of books is empty, prints all found authors using also, and finally transforms the map
to a list of authors using map.

Lambdas with Receivers


The principle underlying the higher-order functions apply, with, and run—where you refer to
the object it was called on via this instead of it—is called lambdas with receivers. Here, receiver
refers to the object on which the function is called, for instance authorToBooks in the case of
apply in Listing 3.47. The lambda expression that you pass in as an argument is attached to the
receiver. This lets you effectively write your lambda expression as if you were writing it inside the
receiver type: You can access members of the receiver class directly. In other words, you can write
the lambda as if it was an extension function on the receiver. In fact, lambdas with receivers use a
syntax similar to extension functions, as shown in Listing 3.48.

Listing 3.48 Signatures of let and run


fun <T, R> T.let(block: (T) -> R): R = block(this) // Lambda with parameter (T) -> R
fun <T, R> T.run(block: T.() -> R): R = block() // Lambda with receiver T.() -> R
Lazy Sequences 61

The signature difference between the two is only in the input of the lambda parameter. Both let
and run are defined as extension functions on a generic type T. However, let accepts a function
(block) that has T as a parameter. It calls that function on the object of type T on which let was
called. Thus, the argument is accessible as it from inside the lambda.

For run, on the other hand, the passed-in lambda is declared with T as its receiver using the syntax
T.() -> R. This effectively makes the lambda an extension function on T and you can imple-
ment the lambda like an extension function, meaning you can access members of T via this.
This allows the implementation of run to call this.block() or simply block().

This is one way to differentiate between the five scoping functions. In fact, there are three dimen-
sions you can differentiate.
 Whether the scoping function’s parameter uses a lambda with receiver or a “normal”
lambda with parameter—and therefore if you use this or it inside the lambda,
respectively
 Whether the scoping function returns the object on which it was called or the lambda’s
return value
 Whether the scoping function itself is an extension function or accepts a parameter (with
is the only one that accepts an explicit parameter)

Figure 3.2 provides a visual representation of this as a reference to quickly recap the differences
between the scope functions.

Extension
function of T?
Yes No
Lambda’s
input

Receiver apply run with

Parameter also let

Object it was
Output Result of the lambda
called on

Figure 3.2 Overview of scoping functions

Lazy Sequences
Having covered higher-order functions and lambda expressions, the last cornerstone of func-
tional programming that is left to discuss is lazy evaluation. More specifically, this section covers
sequences in Kotlin—a data structure using lazy evaluation.
62 Chapter 3 Functional Programming in Kotlin

Java Note
As mentioned, Kotlin’s sequences work the same way as streams from Java 8. The reason
behind reinventing the wheel here instead of reusing Java’s streams was to support them on all
platforms, even those that do not support Java 8 (primarily Android).

Lazy Evaluation
The concept of lazy evaluation is all about evaluating expressions only if and when it becomes
necessary at runtime. This is in contrast to eager evaluation, where every expression is eagerly
evaluated even if the result or part of it is never used.

The main benefit of lazy evaluation is that it can improve performance when working with large
collections or when performing expensive operations on them when only part of the results are
actually required. This performance benefit stems from two things: avoiding unnecessary compu-
tations and avoiding the creation of list objects to hold intermediate results.

Listing 3.49 shows a simple example in which animals could be a normal collection (list, set, or
map) using eager evaluation or a sequence using lazy evaluation. How these would behave differ-
ently is explained below, but the way you can work with them using higher-order functions is the
same in either case.

Listing 3.49 Filtering and Mapping a Collection or Sequence


// ‘animals’ stores the strings "Dog", "Cat", "Chicken", "Frog"
// ‘animals’ may be an eager collection or a lazy sequence but usage is the same:
animals.filter { it.startsWith("C") }
.map { "$it starts with a ‘C’" }
.take(1)

This code filters the animals to keep only those starting with a “C,” maps these to a different
string, and finally takes the first result. Let’s explore how this code behaves in both eager evalua-
tion and lazy evaluation:
 In eager evaluation, if animals is a collection or an array, the code would first filter all
four elements and then store the intermediate result in a newly created list object. After
that, it performs the map on each element of this intermediate result and produces another
intermediate list. Finally, it takes the first element from the mapped list. In total, two
intermediate objects are created, and four filter operations and two map operations are
performed—even though ultimately just one element is used.
 In lazy evaluation, if animals is a sequence, each element goes through the function chain
one by one. Thus, it would first filter out "Dog" because it doesn’t start with a “C” and
immediately proceed with the next element. It then moves on to "Cat", which passes the
filter. Then, it maps this element and calls take(1). With this, the query is fulfilled and no
more operations are performed—the last two elements are not touched.

Kotlin’s main language feature supporting lazy evaluation like this is sequences.
Lazy Sequences 63

Using Lazy Sequences


This subsection covers three ways to get hold of a sequence. First, there is a helper function
sequenceOf, consistent with the helpers that create collections. It is shown in Listing 3.50.

Listing 3.50 Lazy Sequence from Scratch


val sequence = sequenceOf(-5, 0, 5)
println(sequence.joinToString()) // -5, 0, 5

Second, when you already have a (potentially large) collection, you can transform this collection
into a sequence by calling asSequence. All the higher-order functions discussed in this chapter
are available for sequences as well. For instance, imagine you had a list of all large cities in the
world and want to print those cities whose names start with “W,” as shown in Listing 3.51.

Listing 3.51 Lazy Sequence from Collection


val output = cities.asSequence() // Transforms eager list into a lazy sequence
.filter { it.startsWith("W") }
.map { "City: $it" }
.joinToString()

println(output) // City: Warsaw, City: Washington, City: …

There are several things to notice in Listing 3.51. First, the call to asSequence transforms the
normal collection into a lazy sequence to make sure all operations are performed lazily. Second,
the rest looks just like it did for normal, eagerly evaluated, collections. Third, you differentiate
between intermediate and terminal operations. Intermediate operations are all operations that
again return a sequence, such as filter, map, sort, and fold. In contrast, terminal operations
are typically the last operation in a chain and may return anything but a sequence. Here,
joinToString is called as the terminal operation to retrieve a string. Other common terminal
operations include toList, toSet, toMutableList, toMutableSet, first, last, min, and
max. Without a terminal operation, a lazy sequence performs no computations at all.

The third way in which lazy sequences may be used is to create a sequence from the get-go instead
of transforming an existing collection into a sequence. For this, the helper function generate-
Sequence is used, which takes in a seed element and a function to calculate the next element
based on its predecessor, as shown in Listing 3.52.

Listing 3.52 Lazy Sequences with generateSequence


val naturalNumbers = generateSequence(0) { it + 1 } // Next element = previous + 1
val integers = generateSequence(0) { if (it > 0) -it else -it + 1 }
64 Chapter 3 Functional Programming in Kotlin

The first line uses zero as the seed element to start the sequence. Each next element adds one to
its predecessor, resulting in the sequence 0, 1, 2, 3, … of natural numbers. The second line takes it
one step further to define the sequence 0, 1, −1, 2, −2, 3, −3, … of all integers. This shows that each
element in the sequence is calculated based on the previous one, and only when necessary.

Note
With this, you defined your first infinite sequence. This is only possible due to lazy evaluation;
there is no way to actually store a data structure of all natural numbers in memory.

Take and Drop


The functions take and drop are simple but essential, and it is worth noting how to best use
them. As briefly mentioned, take(n) returns the first n elements of a collection or sequence. Its
counterpart drop(n) returns the tail of the sequence without its first n elements, as shown in
Listing 3.53. Note that both leave the original sequence unchanged.

Listing 3.53 Using take and drop


val cities = listOf("Washington", "Houston", "Seattle", "Worcester", "San Francisco")
val firstTwo = cities.take(2) // Washington, Houston
val rest = cities.drop(2) // Seattle, Worcester, San Francisco
firstTwo + rest == cities // true

When chaining function calls, especially when they contain take or drop calls, order of execu-
tion can greatly affect performance. It is a good practice to reduce the size of large collections
early on to save unnecessary operations—and this is what take and drop do. Although this is
also a good practice when working with lazy sequences, it is crucial when using eager collections
because these will otherwise perform all operations on all elements. As an example, reconsider
Listing 3.51 and say you only want to print the first 20 cities that match your selection, as done in
Listing 3.54.

Listing 3.54 Using take (and drop) Early


// Not good
cities.filter { it.startsWith("W") }
.map { "City: $it" } // Calls map before take (could be a million map calls)
.take(20) // Takes only the first 20 results
.joinToString()

// Better
cities.filter { it.startsWith("W") }
.take(20) // Reduces the size of the collection earlier
.map { "City: $it" } // Calls map at most 20 times
.joinToString()
Lazy Sequences 65

You cannot place the take call before the filter because you want the first 20 cities starting with
“W.” But you can place it before the map call. For instance, if 2,000 cities make it through the
filter, this avoids 1,980 unnecessary applications of the map function. Thus, the performance
benefits depend on how many operations can be saved and how early in the chain the collection
can be shrunk. If you can greatly reduce the size early in the chain, eager collections are likely to
perform better than lazy sequences. In other cases, lazy sequences can be the better choice.

Performance of Lazy Sequences


To illustrate the difference between lazy sequences and eager collections, let us look again at the
minimal example in Listing 3.55 that performs a chain of functions on a list of cities.

Listing 3.55 Eager Evaluation on Collections


val cities = listOf("Washington", "Houston", "Seattle", "Worcester", "San Francisco")

cities.filter { println("filter: $it"); it.startsWith("W") } // Washington,Worcester


.map { println("map: $it"); "City: $it" } // City: Washington, City: Worcester
.take(2) // Should better be called before ‘map’

If using normal collections, each function in the chain is fully evaluated, the intermediate result
is stored in an object, and only then is the next function executed.

1. First, filter iterates over the whole collection, leaving only Washington and Worcester.
These are stored in a new list as an intermediate result.

2. Then, map is called on this intermediate list of two elements, mapping each to a string with
a "City: " prefix. Another list is created to store this next intermediate result.

3. Finally, take returns another newly created list as the result.

Thus, this produces the output shown in Listing 3.56 due to eager evaluation.

Listing 3.56 Output for Eager Evaluation


filter: Washington
filter: Houston
filter: Seattle
filter: Worcester
filter: San Francisco // Eagerly filtered all elements
map: Washington
map: Worcester

With sequences, the processing is very different. Each element goes through the chain one by
one. Listing 3.57 shows the same example using sequences.
66 Chapter 3 Functional Programming in Kotlin

Listing 3.57 Lazy Evaluation on Sequences


val cities = listOf("Washington", "Houston", "Seattle", "Worcester", "San Francisco")

cities.asSequence()
.filter { println("filter: $it"); it.startsWith("W") }
.map { println("map: $it"); "City: $it" }
.take(2) // Should still better be called before map
.toList()

Here, a call to asSequence is included and also a call to toList as the terminal operation.
Without such a terminal operation, no computation would be performed due to the laziness.
At this point, the take function comes into play. The code in Listing 3.57 results in the output
shown in Listing 3.58.

Listing 3.58 Output for Lazy Evaluation


filter: Washington
map: Washington // Passes element on to next step immediately
filter: Houston
filter: Seattle
filter: Worcester
map: Worcester // Two elements found, "San Francisco" not processed anymore (lazy)

First, you can see that each element goes through the chain of functions one by one. Thus, there
is no need to store intermediate results. Second, it becomes obvious that map is only performed
on elements passing the filter (this is also the case in eager evaluation). Lastly, due to taking only
two elements, the last element (San Francisco) is not processed at all. This is different from eager
evaluation and is the second reason why sequences can improve performance.

Now it becomes clear why sequences tend to improve performance for large collections or when
performing expensive operations that are partly unnecessary.
 The larger the collection is, the larger are the intermediate list objects created after each
step in eager evaluation. Lazy evaluation creates no such intermediate results.
 The more expensive the operations are, the more computation time can be saved by
performing them lazily—thus skipping all unnecessary operations.

These are your rules of thumb regarding when you may want to prefer sequences over normal
collections. However, Kotlin collections are implemented very efficiently. So, which works better
in a specific use case should be evaluated beforehand. To get a rough estimate, Kotlin’s built-in
function measureTimeMillis can be used. This is another higher-order function that takes in a
block of code as a lambda expression and returns the time in milliseconds that was needed to run
that code block.
Summary 67

Summary
In this chapter, you explored how functional programming emphasizes immutability and exten-
sive use of functions as first-class members of the language. Several examples of predefined and
self-written higher-order functions demonstrated the power of this concept for code modulariza-
tion and writing concise code.

You saw many examples combining these with lambda expressions to define anonymous func-
tions, typically to create the arguments or return values for higher-order functions. One Kotlin-
specific feature surrounding lambdas is the implicit it variable for lambdas with only one
parameter. Another is the possibility to write lambdas that appear as the last argument outside
of the function call parentheses. You saw how this can result in functions that look like language
keywords, like many from the Kotlin standard library.

Lastly, this chapter introduced the concept of lazy evaluation and Kotlin’s lazy sequences as one
example of this. You saw how their performance compares to normal collections and learned in
which cases you may prefer sequences over collections.
This page intentionally left blank
4
Object Orientation in Kotlin

Complexity has nothing to do with intelligence, simplicity does.


Larry Bossidy

Object orientation (OO) has become a fixture in software development since its incarnation in
languages like Simula and Smalltalk in the 1960s and 1970s. This has led to a majority of develop-
ers who tackle problems mostly in an object-oriented way, and many modern languages continue
to incorporate the concepts of OO, often combined with other paradigms. Kotlin incorporates
both OO and functional paradigms. In this chapter, you learn this second pillar of the language.
Some of the presented concepts are known from other object-oriented languages. But Kotlin
introduces various useful features that languages like C# and Java don’t have, enabling Kotlin
users to write programs quickly and with little boilerplate code.

Classes and Object Instantiation


Basic class declarations are introduced using the class keyword followed by the name of the
class. In Kotlin, there’s no new keyword for object instantiation, as demonstrated in Listing 4.1.

Listing 4.1 Class Declaration and Instantiation


class Task {
// Implement class here...
}

val laundry = Task() // Instantiates object of type Task

With this, you already saw the two most fundamental entities in OO: classes and objects. Classes act
as blueprints from which you can instantiate specific objects. However, without any properties that
differ between each object, this is hardly useful. So let us introduce some properties in the Task class.
70 Chapter 4 Object Orientation in Kotlin

Properties
To explore how properties work in Kotlin, and how you can best add them to your class, let us
start with a non-ideal way to implement a class with a property as it is done in other languages
(shown in Listing 4.2), and then refactor it step by step to idiomatic Kotlin code.

Listing 4.2 Adding a Property the Hard Way


class Task {
val title: String // Declares a property

constructor(title: String) { // Defines a constructor with one parameter


this.title = title // Initializes the property
}
}

val dishes = Task("Wash dishes") // Calls constructor to create Task object


val laundry = Task("Do laundry") // Calls constructor with a different title

Here, the class contains a member property title, which is initialized inside a constructor that
accepts a String. The constructor is run whenever you instantiate a Task object. Notice how
often you have to mention the title in this code and manually set it in a constructor. To improve
this, you can use a primary constructor instead in Kotlin. It directly follows the class name, as in
Listing 4.3. Constructors are discussed in detail in the section titled, “Primary and Secondary
Constructors.”

Listing 4.3 Using a Primary Constructor


class Task(title: String) { // Primary constructor with one parameter
val title: String = title // Initializes property using constructor argument
}

val laundry = Task("Do laundry") // Calls constructor as before

The parameters of the primary constructor can be used directly inside the class body to instanti-
ate any member properties. Notice also that the compiler can differentiate between the declared
property title and the constructor parameter of the same name. This code is already a lot more
concise, but the idiomatic way uses a shortcut in Kotlin that allows you to “upgrade” a primary
constructor parameter to a property, as in Listing 4.4.

Listing 4.4 Idiomatic Way to Add a Simple Property


class Task(val title: String) // Primary constructor declares property directly

val laundry = Task("Do laundry")


Properties 71

Prefixing a parameter in a primary constructor with either val or var implicitly makes it a
class property and automatically handles its initialization with the given argument, here "Do
laundry". Again, val makes the property read-only and var makes it mutable. Notice that curly
braces can be omitted if the class body is empty.

Properties versus Fields


What is interesting here is that we’re talking about properties, not fields1 as in many other
languages (including Java). You can think of properties as fields extended with getters and setters
that allow access to the underlying field. In other words, fields hold the actual data, whereas prop-
erties form part of the class’ interface to the outside, allowing access to some of the class’ data via
getters and setters. In this way, properties are similar to methods because getters and setters are
in fact methods. But properties are purely for data access and don’t typically perform any logic
besides returning or setting their corresponding data.

Fields should almost always be private, unless they are static or constant. In Java, you do this
manually by marking your field private and defining separate getter and setter methods. In C#,
you can explicitly create properties that usually wrap a private field and define a getter and a
setter. Both languages add boilerplate code around this simple accessor logic. Most of the time,
the code inside these methods follows the same pattern, which is why your IDE can generate
them for you.

The main problem with such boilerplate is not the time it takes to write it—your IDE can gener-
ate it in a second. The problem is that it distracts from the actual logic, and it’s hard to scan for
possible deviations from the standard pattern. Auto-properties in C# help avoid much of this
boilerplate, and Kotlin doesn’t even let you declare fields yourself. Instead, you declare properties.
Accessing a property automatically calls its getter or setter, as shown in Listing 4.5.

Listing 4.5 Property Access Calls Getter or Setter


class Task(var title: String) // Uses ‘var’ now to be able to call the setter

val laundry = Task("Do laundry")


laundry.title = "Laundry day" // Calls setter
println(laundry.title) // Calls getter

The title property is now declared as mutable in order to have a setter. Reassigning the prop-
erty calls its setter, and retrieving its value calls its getter. So while this kind of code would almost
always be bad practice when using fields because it would indicate direct field access, it is perfectly
in accordance with the principle of information hiding in Kotlin because there is no direct field
access, only getter and setter access.

1. Some say “attributes,” but this term is vague and not used in the Java documentation.
72 Chapter 4 Object Orientation in Kotlin

Getters and Setters


When you want to change the default getter or setter implementation, you can do so using a
specialized syntax to override the implicit get and set methods, as in Listing 4.6. In this case,
the shorthand property syntax in the primary constructor is no longer possible because get and
set must directly follow the declaration of their respective property.

Listing 4.6 Custom Getter and Setter


class Task(title: String, priority: Int) {
val title: String = title
get() = field.toUpperCase() // Defines custom getter

var priority: Int = priority


get() = field // Same as default implementation, no need to define
set(value) { // Defines custom setter
if (value in 0..100) field = value else throw IllegalArgumentException("…")
}
}

val laundry = Task("Do laundry", 40)


println(laundry.title) // Calls getter, prints "DO LAUNDRY"
laundry.priority = 150 // Calls setter, throws IllegalArgumentException
println(laundry.priority) // Calls getter, would print 40

Here, the properties are declared inside the class body again, so that they can be followed by
their custom getter and setter implementations. Because title is a read-only val, it only has a
getter and no setter. The getter returns the title in uppercase. For the priority, the getter is just the
default implementation, and the setter only accepts values between 0 and 100.

Every property has an implicit backing field that stores the actual data. Its name is field, and it
can only be accessed inside get or set. In fact, you have to use it to access the property’s value. If
you used get() = title.toUpperCase() in Listing 4.6 instead, you’d theoretically end up in
an infinite recursion because title.toUpperCase would again call the getter.

In practice, Kotlin would instead consider the title property to have no backing field. Thus,
you could no longer initialize it with the title from the constructor (as in the second code line).
Properties without backing fields calculate their values on the fly, instead of storing their data in
a field. Note that the variable name field is fixed, whereas the parameter name value for the
setter is only a convention.

Late-Initialized Properties
Trying to follow the good practices of using immutability and avoiding nullability is not always
easy. Especially the latter is a shift in mindset when migrating from a language without null-
safety. One situation where following these principles can look difficult is when you have
Properties 73

read-only and non-null properties. These have to be initialized right inside the constructor, but
doing this is often inconvenient or just unnecessary. Common examples where initialization
directly at declaration site is unnecessary are test cases where objects are initialized by a test setup
method, via dependency injection, or in onCreate on Android.

In Kotlin, the solution for such situations can often be found in specific language features. The
situation above can be solved using late-initialized properties. These allow you to defer the initial-
ization to a later point, as in Listing 4.7.

Listing 4.7 Using Late-Initialized Properties


class CarTest {
lateinit var car: Car // No initialization required here; must use ‘var’

@BeforeEach
fun setup() {
car = TestFactory.car() // Re-initializes property before each test case
}
// ...
}

This way, there’s no need to make the car property nullable, making it much easier to work with.
The cost for this is that lateinit properties must always be mutable vars (this is necessary here
anyway to reassign it inside the test setup method). There are more restrictions for properties with
which the lateinit keyword can be used.

1. The property must be declared inside a class, and not in its primary constructor. Properties
in the primary constructor will be initialized anyway, so lateinit would be useless. The
lateinit keyword is for properties that you cannot initialize in the constructor in a useful
way.

2. The property must not have a custom getter or setter. With a custom accessor, the compiler
could no longer infer whether an assignment actually initializes the property—imagine an
empty setter, for instance.
3. The property must be non-nullable. Otherwise, you could simply initialize with null
instead. The purpose of lateinit is to avoid exactly this.

4. The property must not have a type that is mapped to a primitive Java type at runtime, such
as Int, Double, or Char. This is because, under the hood, lateinit still has to use null,
and this is impossible with primitive types.

What if you accidentally access the property before it’s initialized? The compiler will spit out an
UninitializedPropertyAccessException with an appropriate error message that leads you
to the source of the exception. To check beforehand if it’s initialized, you could use this::car.
isInitialized from within the CarTest class. However, you can only use this from scopes that
have access to the property’s backing field.
74 Chapter 4 Object Orientation in Kotlin

Delegated Properties
Although most property accessors perform no additional logic other than returning or setting
a value, there are several types of more sophisticated accessors that are commonly used—for
instance, lazy and observable properties. In Kotlin, property accessors that perform such logic can
delegate to a separate implementation that provides the logic. This makes the accessor logic reus-
able because the delegate encapsulates it.

Kotlin achieves this via delegated properties, as shown in Listing 4.8.

Listing 4.8 Syntax of Delegated Properties


class Cat {
var name: String by MyDelegate() // Delegates to object of class MyDelegate
}

Here, instead of giving the name property a concrete value, you only indicate that getting
and setting its value is handled by an object of the class MyDelegate. To specify the delegate,
you use the by keyword. The delegate class must have a method getValue and, for mutable
properties, also setValue. Listing 4.9 shows an implementation of MyDelegate that demon-
strates the structure. Although it’s not required, I recommend you implement the interface
ReadOnlyProperty or ReadWriteProperty for read-only and mutable properties, respectively.
ReadOnlyProperty requires you to implement getValue, and ReadWriteProperty addition-
ally requires a setValue method. Interfaces were not discussed yet; this listing is meant as a
reference for how to create custom delegates. If you do not yet understand all concepts used in the
code, you can come back to it after finishing this chapter.

Listing 4.9 Implementing a Custom Delegate


import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

class MyDelegate : ReadWriteProperty<Cat, String> { // Implements ReadWriteProperty


var name: String = "Felix"

// Delegate must have a getValue method (and setValue for mutable properties)
override operator fun getValue(thisRef: Cat, prop: KProperty<*>): String {
println("$thisRef requested ${prop.name} from MyDelegate")
return name
}

override operator fun setValue(thisRef: Cat, prop: KProperty<*>, value: String) {


println("$thisRef wants to set ${prop.name} to $value via MyDelegate")
Properties 75

if (value.isNotBlank()) {
this.name = value
}
}
}

val felix = Cat()


println(felix.name) // Prints "Cat@1c655221 requested name from MyDelegate\n Felix"
felix.name = "Feli" // Prints "Cat@1c655221 wants to set name to Feli via MyDelegate"
println(felix.name) // Prints "Cat@1c655221 requested name from MyDelegate\n Feli"

The exact signature of the interface ReadWriteProperty is ReadWriteProperty<in R, T>,


where R is the class that has the property, and T is the type of the property (this is a generic
class, which is discussed later in this chapter). Here, the property is for the Cat class, and it’s a
String property. Hence, it’s a ReadWriteProperty<Cat, String>. By inheriting from it, you
can generate the method stubs in Android Studio using Ctrl+I (also on Mac). Notice that both
methods must be declared as operator functions in order to be used as a delegate. In contrast, the
override modifier can be omitted if you choose not to implement any of the interfaces. With
this, reading the name property of the Cat class calls MyDelegate::getValue and setting a new
value delegates to MyDelegate::setValue.

Predefined Delegates
As mentioned, the motivation behind delegated properties is to encapsulate common accessor
logic in a reusable way. Thus, this is exactly what was done in the Kotlin standard library in the
form of several predefined delegates.

Lazy Properties
The first predefined delegate allows you to easily implement lazy properties. These compute their
value only when accessed the first time and then they cache it. In other words, the value is not
computed until it’s used. This is why lazy properties are particularly useful when computing the
value is expensive and there’s a chance that it’s not used. Listing 4.10 augments the Cat class to
exemplify this.

Listing 4.10 Using Lazy Properties


import java.time.LocalDate
import java.time.temporal.ChronoUnit

class Cat(val birthday: LocalDate) {


val age: Int by lazy { // Lazy property, computed on demand only if necessary
println("Computing age...")
76 Chapter 4 Object Orientation in Kotlin

ChronoUnit.YEARS.between(birthday, LocalDate.now()).toInt() // Computes age


}
}

val felix = Cat(LocalDate.of(2013, 10, 27))


println("age = ${felix.age}") // Prints "Computing age...\n age = 5" (run in 2018)
println("age = ${felix.age}") // Prints "age = 5"; returns cached value

Here, the cat’s age is lazily computed only when accessed for the first time. The higher-order
function lazy accepts a lambda expression as its last parameter, thus allowing this syntax. Inside
the lambda, you define how the value is computed, and the expression in the last line defines the
return value.

The lazy function is thread-safe by default. If you don’t need thread safety, you can pass in an
optional safety mode as the first parameter, for instance, lazy(LazyThreadSafetyMode.NONE)
for a non-thread-safe implementation.

Note
On Android, lazy properties are useful to start up activities more quickly and prevent repetitive
code. For example, instead of using findViewById explicitly in onCreate, you can define
your UI properties as lazy:
val textView: TextView by lazy { findViewById<TextView>(R.id.title) }

This can be further encapsulated into a bind extension function:

fun <T: View> Activity.bind(resourceId: Int) = lazy { findViewById<T>(resourceId) }


val textView: TextView by bind(R.id.title)

You can use lazy properties in the same way for other resources like strings or drawables.
Additionally, you can use them to delay creation of heavy objects to the point where
they are used. For example, you can use this to avoid performing too many I/O
operations on startup and thus prevent the app from not responding or even
crashing.

Observable Properties
Observable properties are another common pattern that can be realized using delegation. In
Kotlin, it is predefined as Delegates.observable, which accepts an initial value and a change
handler function that is executed whenever the property’s value changes. In other words, it
observes the property. Listing 4.11 shows how to use observable properties by further extending
the class Cat. It also introduces an enumeration class with the possible moods of a cat. Such
enumerations are discussed in more detail later in this chapter.
Properties 77

Listing 4.11 Using Observable Properties


import java.time.LocalDate
import kotlin.properties.Delegates

class Cat(private val birthday: LocalDate) {


// …
var mood: Mood by Delegates.observable(Mood.GRUMPY) { // Observable property
property, oldValue, newValue -> // Lambda parameters with old and new value

println("${property.name} change: $oldValue -> $newValue")


when (newValue) {
Mood.HUNGRY -> println("Time to feed the cat…")
Mood.SLEEPY -> println("Time to rest…")
Mood.GRUMPY -> println("All as always")
}
}
}

enum class Mood { GRUMPY, HUNGRY, SLEEPY } // Enums are explained later

val felix = Cat(LocalDate.of(2013, 11, 27))


felix.mood = Mood.HUNGRY // "mood change: GRUMPY -> HUNGRY\n Time to feed the cat…"
felix.mood = Mood.SLEEPY // "mood change: HUNGRY -> ASLEEP\n Time to rest…"
felix.mood = Mood.GRUMPY // "mood change: ASLEEP -> GRUMPY\n All as always"

If you look into the implementation, you’ll see that Delegates.observable returns a
ReadWriteProperty. Its first parameter defines the initial value of the property. Here, it is
Mood.GRUMPY. On each value update, the delegate prints the change. Inside the when expression,
an action is taken depending on the new value. This is where you’ll usually notify any observers
that are interested in changes to this property. This is useful to update a UI whenever its underly-
ing data is changed. For instance, delegated properties provide a more natural way to implement
view binding.

Vetoable Properties
When using observable properties, the handler is executed after the value of the property has
already been updated. If you want the same behavior but be able to prevent the new value from
being accepted, you can use vetoable properties instead.

For this, you use Delegates.vetoable. This works the same way as Delegates.observable
but you have to return a Boolean value that indicates whether to accept the new value or not. If
you return false, the property’s value will not be updated.
78 Chapter 4 Object Orientation in Kotlin

Delegating to a Map
Lastly, Kotlin allows you to delegate properties to a map, which maps the property names to their
respective values. When would you use this? Most commonly when parsing objects from a non-
object-oriented representation such as JavaScript Object Notation (JSON). Imagine you’re retriev-
ing information about persons in JSON format from an API. Listing 4.12 shows how you can
translate this back to an object using a map as a delegate.

Listing 4.12 Delegating Properties to a Map


class JsonPerson(properties: Map<String, Any>) {
val name: String by properties // Delegates property to the map
val age: Int by properties // Delegates property to the map
val mood: Mood by properties // Delegates property to the map
}

// Let’s assume this data comes from JSON; keys in the map must match property names
val jsonData = mutableMapOf("name" to "John Doe", "age" to 42, "mood" to "GRUMPY")

// You may need to preprocess some data (requires MutableMap)


jsonData["mood"] = Mood.valueOf(jsonData["mood"] as String) // ‘valueOf’ is built-in

// Creates an object from the map


val john = JsonPerson(jsonData) // Properties are matched to the keys in the map
println(john.name) // Prints "John Doe"
println(john.age) // Prints 42
println(john.mood) // Prints "GRUMPY"

// Read-only property changes if backing map changes


jsonData["name"] = "Hacker"
println(john.name) // Prints "Hacker"

Using maps is still in line with the premise that delegates must have a getValue method. For
maps, these accessors are defined as extension functions in the file MapAccessors.kt. This
way, the property simply delegates its accessors to the map. For instance, john.name delegates to
properties["name"].

To support mutable properties, you have to use a MutableMap as the delegate to have a setValue
method. However, be aware that even read-only properties that delegate to a mutable map can
unexpectedly change their value if the backing map is changed. This is illustrated by the last two
lines in Listing 4.12. To prevent this, you could accept a read-only map for the read-only proper-
ties and a mutable map for the mutable properties. If changes are not frequent, you could also use
Properties 79

only read-only properties and reinstantiate the whole object from the map whenever you want to
change a value.

Using Delegated Implementations


The delegation pattern is not only useful for properties but for method implementations as well.
Its idea is that you can implement an interface by delegating to an existing implementation of
it instead of implementing it all over again, and you can do so without boilerplate code. As an
example, in Listing 4.13, the class Football implements a Kickable interface by delegating to
an existing implementation.

Listing 4.13 Delegating to an Implementation Using by


interface Kickable {
fun kick()
}

// Existing implementation of Kickable


class BaseKickHandler : Kickable {
override fun kick() { println("Got kicked") }
}

// Football implements interface by delegating to existing impl. (zero boilerplate)


class Football(kickHandler: Kickable) : Kickable by kickHandler

Here, the Football class implements Kickable without defining any explicit overrides. Instead,
it delegates to an existing implementation using by. This makes the compiler generate all required
methods and delegate them to the delegate object that is passed into the constructor, here kick-
Handler. Listing 4.14 illustrates the generated code, as well as the code that you’d have to write
without native support for delegation.

Listing 4.14 Delegated Implementation


// Implements interface with manual delegation
class Football(val kickHandler: Kickable) : Kickable {
override fun kick() {
kickHandler.kick() // Trivial forwarding; necessary for every interface method
}
}

This is boilerplate code, and it grows with the number of methods that must be overridden. For
instance, if you wanted to have a delegating mutable set that you can safely use for inheritance,
you can do so in Kotlin in a single line, as shown in Listing 4.15.
80 Chapter 4 Object Orientation in Kotlin

Listing 4.15 Delegating Mutable Set


open class ForwardingMutableSet<E>(set: MutableSet<E>) : MutableSet<E> by set

To achieve this without delegation using by, you would have to override 11 methods, all of them
just boilerplate. This is another way in which Kotlin allows for more concise and expressive code.

Methods
At this point, you know how to add data to your classes and control access to it using getters,
setters, and delegated properties. As you know, there are two major components to classes in OO,
and data is only one of them. In this section, you’ll learn how to add the second component,
behavior, to your classes in the form methods.

Methods are essentially the same as functions, except that they’re nested into a class. Thus, you
can apply all you know about functions from the previous chapters here. This includes the syntax;
defining parameters and return types; using the shorthand notation, default values, and named
parameters; and creating inline, infix, and operator functions. In the case of infix functions, the
type of the first parameter is automatically the class containing the function. Listing 4.16
provides an overview.

Listing 4.16 Declaring and Calling Methods


class Foo {
fun plainMethod() { … }
infix fun with(other: Any) = Pair(this, other)
inline fun inlined(i: Int, operation: (Int, Int) -> Int) = operation(i, 42)
operator fun Int.times(str: String) = str.repeat(this)
fun withDefaults(n: Int = 1, str: String = "Hello World") = n * str
}

val obj = Foo()


obj.plainMethod()
val pair = obj with "Kotlin"
obj.inlined(3, { i, j -> i * j }) // 126
obj.withDefaults(str = "Hello Kotlin") // "Hello Kotlin"
with(obj) { 2 * "hi" } // Uses ‘with’ to access extension

What differentiates methods from functions is that they live in the realm of a class hierarchy.
Their purpose is to manipulate a class’ data and provide a well-defined interface to the rest
of the system. Being part of a class hierarchy, methods support features like overriding and
polymorphism, both of which are cornerstones of OO. Overriding means you can redefine the
Methods 81

implementations of a parent class in subclasses to adapt their behavior. Polymorphism means that
if you call a method on a variable, it executes the method definition of the (sub)class that is actu-
ally stored in that variable, which may vary from the implementation in the parent type. This
behavior is also called dynamic dispatch.

Extension Methods
In Kotlin, you can define extensions inside a class. Let’s call these extension methods. Recall that,
inside an extension function, you can access the properties of the extended class without qualifier
(as if you were writing that function inside the extended class). In the case of extension methods,
you can additionally access all members of the containing class without qualifier, as all methods
can. Thus, you must differentiate between the dispatch receiver (the containing class) and the
extension receiver (the class you extend). If there’s a clash because both define a method foo(),
the extension receiver takes precedence. You can still access the dispatch receiver’s foo() using a
qualified this@MyClass. Listing 4.17 demonstrates all this.

Listing 4.17 Extension Methods


class Container {
fun Int.foo() { // Extends Int with a ‘foo’ method
println(toString()) // Calls Int.toString (like this.toString())
println(this@Container.toString()) // Calls Container.toString
}

fun bar() = 17.foo() // Uses extension method


}

Container().bar() // Prints "17\n Container@33833882"

Here, the containing class defines an extension method on Int called foo that first calls
toString without qualifier—equivalent to calling this.toString. Because both Container
and Int have a toString method (every class inherits one), the extension receiver (Int) takes
precedence. After that, the dispatch receiver’s toString implementation is called using the
qualified this@Container.

Tip
Extension methods are useful to limit the scope of extensions to a class (and its child
classes). As projects grow, extensions declared on top-level become increasingly prob-
lematic because they pollute the global namespace with lots of potentially irrelevant
functions—autocomplete takes longer and becomes less helpful. Limiting scope is a good
practice in general and crucial when working with extensions.
82 Chapter 4 Object Orientation in Kotlin

Nested and Inner Classes


Kotlin not only lets you create nested classes, it even provides a convenient way to let them access
properties and methods from their containing class: so-called inner classes, as demonstrated in
Listing 4.18.

Listing 4.18 Comparing Nested and Inner Classes


class Company {
val yearlyRevenue = 10_000_000

class Nested {
val company = Company() // Must create instance of outer class manually
val revenue = company.yearlyRevenue
}

inner class Inner { // Inner class due to ‘inner’ modifier


val revenue = yearlyRevenue // Has access to outer class members automatically
}
}

In normal nested classes, you must first get a reference to the containing class to access any of its
members. Inner classes allow you to access these directly. Under the hood, the compiler simply
generates the same code as shown in the nested class.

Primary and Secondary Constructors


Kotlin differentiates between primary and secondary constructors. When you have multiple
constructors in other languages, you often think of one of them as being the primary one, but
Kotlin makes this explicit and provides a pragmatic way to declare properties directly in the
primary constructor.

Primary Constructors
You already briefly saw a primary constructor when you learned how to add properties to a class.
Now, let’s dive a little deeper. The primary constructor directly follows the class name (and possi-
ble modifiers) as demonstrated in Listing 4.19. Parameters are defined the same way as in normal
methods.

But where do you place initialization logic that you’d normally write into the constructor?
For this purpose, you use an init block in Kotlin, also shown in Listing 4.19. So, the primary
constructor is split into two parts: the parameters in the class header, and the constructor logic
in the init block. Be aware that normal constructor parameters (without val or var) can be
accessed only inside this init block and in property initializers but not in class methods because
they are not properties of the class.
Primary and Secondary Constructors 83

Listing 4.19 Using a Primary Constructor


class Task(_title: String, _priority: Int) { // Defines regular parameters
val title = _title.capitalize() // Uses parameter in initializer
var priority: Int

init {
priority = Math.max(_priority, 0) // Uses parameter in init block
}
}

Here, underscores are used to differentiate the constructor parameters from the properties and
avoid name clashes. The _title parameter is used in an initializer and the _priority parameter
is used in the init block to initialize the corresponding properties.

If the constructor has modifiers, the constructor keyword must be used and directly precedes
the parameter list as in Listing 4.20.

Listing 4.20 Adding Modifiers to a Primary Constructor


class Task private constructor(title: String, priority: Int) { … }

The explicit constructor keyword is required to set the constructor’s visibility and to add
annotations or other modifiers. A common reason is dependency injection via @Inject
constructor(…) using a tool like Dagger.

Next, Kotlin provides a concise way to upgrade the parameters of a primary constructor to proper-
ties of the class by prefixing them with val or var, producing read-only and mutable properties,
respectively (as shown in Listing 4.21).

Listing 4.21 Upgrading Primary Constructor Parameters to Properties


class Task(val title: String, var priority: Int) { // Parameters are now properties
init {
require(priority >= 0) // Uses property in init block
}
}

Notice that this code is not equivalent to Listing 4.19: You can no longer capitalize the title
because the title property is read-only and thus fixed to whatever value is passed into the
constructor. Still, for simple properties, using val and var inside the primary constructor is the
idiomatic—and most concise—way to introduce properties.
84 Chapter 4 Object Orientation in Kotlin

Secondary Constructors
Classes may have any number of secondary constructors, including zero. But how do they differ
from primary constructors? Think of the primary constructor as the main interface for object
creation. In Kotlin, all secondary constructors must delegate to the primary constructor if one
exists. Then, you can be sure that the primary constructor is executed on every object creation.
Secondary constructors provide alternative interfaces for object creation that transform the input
data and delegate to the primary constructor, as in Listing 4.22.

Listing 4.22 Combining Primary and Secondary Constructors


class Task(val title: String, var priority: Int) { // Primary
constructor(person: Person) : this("Meet with ${person.name}", 50) { // Secondary
println("Created task to meet ${person.name}")
}
}

Delegation to the primary constructor is achieved by extending the secondary constructor with
a colon followed by this(…) to call the primary constructor. Secondary constructors can have a
constructor body; only the primary constructor uses the init block.

In case you don’t consider one of the constructors to be the primary one, alternately, you can use
only secondary constructors. However, note that you can’t use the shorthand to create class prop-
erties that way.

Tip
Do not use secondary constructors to implement optional constructor parameters as in the
so-called telescoping (anti-)pattern:

class Food(calories: Int, healthy: Boolean) {


constructor(calories: Int) : this(calories, false)
constructor(healthy: Boolean) : this(0, healthy)
constructor() : this(0, false)
}

This is obsolete thanks to default parameter values that allow you to concisely implement
optional parameters:

class Food(calories: Int = 0, healthy: Boolean = false)

Inheritance and Overriding Rules


Inheritance is one of the cornerstones of OO. At its core, OO is about abstraction and program-
ming the differences between classes. This is enabled by inheriting shared logic from a parent
Inheritance and Overriding Rules 85

class that represents an abstraction of its children. Overriding rules specify how inheritance
works, what can be inherited, and which logic can be overridden.

In order to follow along this section, there are three terms that you should know. All of these may
apply to classes, properties, and methods.

 Being abstract means that the class or member is not fully implemented—the remaining
implementation is left for child classes to provide.
 Being open means that the class or member is fully implemented and therefore ready to be
instantiated (class) or accessed (property or method), but it allows child classes to override
the implementation to adjust to their needs.
 Being closed means that the class or member is fully implemented and ready to be used, and
doesn’t allow child classes to override its implementation. For a class, this means it cannot
have subclasses. For members, it means they cannot be overridden in subclasses.

Kotlin follows the closed-by-default principle. This means that classes and their members are by
default closed unless you explicitly make them abstract or open.

To understand inheritance, this section explores common entities involved in inheritance,


namely interfaces, abstract classes, and open classes in Kotlin, and how you can use them for
inheritance.

Interfaces
Interfaces form the highest level of abstraction in your code. They define capabilities that users
can rely on without restricting how implementing classes realize that capability. Typically, they
only define a few abstract methods to indicate the capabilities, as done in Listing 4.23.

Listing 4.23 Defining an Interface


interface Searchable {
fun search() // All implementing classes have this capability
}

In Kotlin, interfaces can have default implementations. So although interface members are
typically abstract as in Listing 4.23, they may have a default implementation. Listing 4.24 shows
properties and methods both with and without default implementations. Although interfaces
can contain properties, they cannot have state. Thus, properties are not allowed to have a backing
field. That’s why all properties are either abstract or have accessors that use no backing field, such
as the getter in Listing 4.24. Because non-abstract properties must not have a backing field, they
must be read-only.
86 Chapter 4 Object Orientation in Kotlin

Listing 4.24 Defining an Interface with Default Implementations


interface Archivable {
var archiveWithTimeStamp: Boolean // Abstract property
val maxArchiveSize: Long // Property with default impl., thus must be val
get() = -1 // Default implementation returns -1

fun archive() // Abstract method


fun print() { // Open method with default implementation
val withOrWithout = if (archiveWithTimeStamp) "with" else "without"
val max = if (maxArchiveSize == -1L) "∞" else "$maxArchiveSize"
println("Archiving up to $max entries $withOrWithout time stamp")
}
}

The Archivable interface defines which capabilities any of its implementing classes must at least
provide, namely archiving and printing. In an interface, any method without body is implicitly
abstract. The same goes for properties without accessors. All other methods and properties are
implicitly open, meaning they can be overridden as well. Thus, all members of interfaces can be
overridden in implementing classes.

Java Note
Java 8 introduced interfaces with default method implementations as well.2

A class can implement any number of interfaces using the syntax shown in Listing 4.25.

Listing 4.25 Inheriting from Interfaces


class Task : Archivable, Serializable { // Implements multiple interfaces
override var archiveWithTimeStamp = true
override fun archive() { … }
}

Here, the class Task implements the two interfaces Archivable and Serializable. By conven-
tion, the colon is surrounded by spaces on both sides here, in contrast to when it’s used to define
variable types or return types. The override modifier is required to override a property or
method from a superclass. This way, overriding is always explicit and cannot happen accidentally.

You should use default implementations in interfaces judiciously because they blur the line
between interfaces and abstract classes.

2. https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html
Inheritance and Overriding Rules 87

Abstract Classes
Abstract classes are one abstraction level below interfaces. Like interfaces, they are used to define
abstract methods and properties but typically already contain more concrete implementa-
tions and can carry state. Their purpose is to encapsulate the similarities of their child classes.
They cannot be instantiated themselves because they don’t yet represent a usable entity of the
application—some members may still be abstract and therefore unusable. Abstract classes are
introduced using the abstract modifier as shown in Listing 4.26.

Listing 4.26 Abstract Classes and Overriding


abstract class Issue(var priority: Int) {
abstract fun complete() // Abstract method
open fun trivial() { priority = 15 } // Open method
fun escalate() { priority = 100 } // Closed method
}

class Task(val title: String, priority: Int) : Issue(priority), Archivable {


// …
override fun complete() { println("Completed task: $title") } // Required override
override fun trivial() { priority = 20 } // Optional override
// Cannot override ‘escalate’ because it is closed
}

Here, Issue represents entities with a priority that can be completed, such as tasks, meetings,
or daily routines. Inheriting from an abstract class works similarly to implementing an interface,
but the class name must be followed by parentheses to call its constructor. Here, this is done via
Issue(priority) in the class header. Any parameters must be passed into the constructor of
the parent class, here priority.

You can access properties and methods of a parent class or interface simply via their name. If their
name clashes with a member of the subclass, you can access the members of the parent class by
qualifying them with super. For instance, Task can access super.priority (the property of
the Issue class) and super.escalate().

Overriding members of parent classes works the same way as overriding members of interfaces.
Kotlin’s closed-by-default principle comes into play here because, in contrast to interfaces,
methods in abstract classes are closed by default. To allow children to override them, you must
use the open modifier in Kotlin. To make them abstract, you must use abstract. Both options
are shown in Listing 4.26.
88 Chapter 4 Object Orientation in Kotlin

Tip
Think of abstract versus open as “must override” versus “can override.” A non-abstract class
must override all abstract members (properties or methods) it inherits, and is allowed to over-
ride any open members. Closed members cannot be overridden.

Note that you can also override properties in primary constructors using class Task(…, over-
ride val priority: Int) but there is no need for it here.

Open Classes
The closed-by-default principle doesn’t only apply to properties and methods in Kotlin but also
to classes: All classes in Kotlin are closed by default, meaning they cannot be inherited from. This
language design choice prompts developers to “explicitly design for inheritance or else prohibit
it” (see Effective Java by Joshua Bloch3). When classes are open by default, they often allow inheri-
tance without being designed for it, which can lead to fragile code.

Java Note
Being closed by default, normal classes in Kotlin correspond to final classes in Java and open
classes in Kotlin correspond to normal Java classes.

Listing 4.27 demonstrates how to declare open classes. It also illustrates that the child of an open
class is again closed by default, so you’ll have to explicitly declare it to be open again to allow
further inheritance.

Listing 4.27 Open Classes and Closed-by-Default Classes


class Closed // Closed class: cannot inherit from this
class ChildOfClosed : Closed() // NOT allowed: compile-time error

open class Open // Open class: can inherit


class ChildOfOpen : Open() // Allowed
class ChildOfChild : ChildOfOpen() // NOT allowed: compile-time error

Classes without an explicit superclass inherit from Any, the superclass of all non-nullables types
in Kotlin. It only defines hashCode, equals, and toString. The reason is that Any is platform-
agnostic and can thus also be used when targeting Kotlin/JS or Kotlin/Native. Other methods
specifically for the JVM are attached via extension functions.

3. https://www.pearson.com/us/higher-education/program/Bloch-Effective-Java-3rd-Edition/
PGM1763855.html
Type Checking and Casting 89

Java Note
Kotlin’s Any type is similar to Object in Java except that it is non-nullable and, as mentioned,
it only defines three methods as members. Any gets mapped to Object in the bytecode.

Overriding Rules
When working with inheritance, there are several rules to consider. Fortunately, they’re logical
when you think about them.

First, due to default methods in interfaces, you may run into the situation where a class
inherits conflicting implementations for a method or property from its superclass and imple-
mented interfaces. In that case, Kotlin forces you to override that member. Inside the override,
you can call a specific parent implementation using the syntax super<MyClass>.foo() or
super<MyInterface>.foo() to differentiate between them.

Second, you’re allowed to override read-only properties with mutable properties but not vice
versa. That’s because a read-only property only implies a getter. So overriding it with a mutable
property is possible because an additional setter can be added. However, a mutable property in a
parent class comes with a setter, which cannot be undone in a child class.

Type Checking and Casting


Type checks and casts in Kotlin are conceptually the same as in other statically typed languages.
But in Kotlin, you have to think about nullability again in order to safely cast objects. For this
section, imagine you’re using the Composite pattern4 and you implemented the classes Component,
Composite, and Leaf, where Component is the abstract parent class of the other two classes.

Type Checking
To check whether an object has a specific type, Kotlin provides the is operator. Listing 4.28
shows how to use it for type checks.

Listing 4.28 Type Checks Using is


val item: Component = Leaf() // ‘item’ is a Leaf at runtime

if (item is Composite) { … } // Checks if ‘item’ is of type Composite at runtime


if (item !is Composite) { … } // Checks if ‘item’ is not a Composite at runtime

The is operator is negated as !is to check whether an object is not of a specific type. Unless the
class structure changes, the second type check is equivalent to item is Leaf in this example
because that is the only other alternative.

4. https://sourcemaking.com/design_patterns/composite
90 Chapter 4 Object Orientation in Kotlin

Type Casting
In statically typed languages like Kotlin, type casting allows you to map (“cast”) the type of an
object into another one, if the types are compatible. In Kotlin, there are several ways to cast
an object because, as always, Kotlin makes you think about nullability and how to handle it.
Consequently, you can cast safely using as? or you can cast without considering the null case
using as. Both are shown in Listing 4.29.

Listing 4.29 Type Casts for Nullable Types


val item: Component? = null

val leaf: Leaf = item as Leaf // TypeCastException


val leafOrNull: Leaf? = item as Leaf? // Evaluates to null
val leafSafe: Leaf? = item as? Leaf // Evaluates to null
val leafNonNull: Leaf = (item as? Leaf) ?: Leaf() // Alternative using elvis op.

This listing shows four ways to cast item to a Leaf object. The first shows that trying to cast a
nullable object to a non-null type unsafely (using as) results in a TypeCastException if the
object is null. To avoid this, the second example explicitly casts to the nullable type Leaf?,
which changes the type of the expression accordingly. The third cast uses the safe cast operator
as? that returns null instead of throwing an exception if the cast fails. To avoid nullability right
at the declaration site of the variable, the fourth cast uses the elvis operator to provide a default
value for the null case.

In the above example, casting to a nullable type and using the safe cast operator are equivalent.
This is no longer the case when trying to cast to a wrong class, as demonstrated in Listing 4.30.

Listing 4.30 Type Casts and ClassCastException


val composite: Component = Composite()

val leafOrNull: Leaf? = composite as Leaf? // ClassCastException


val leafSafe: Leaf? = composite as? Leaf // Evaluates to null

Here, the original object is not even nullable, so casting to a Leaf? does not make the cast any
safer, and the first cast results in a ClassCastException. The difference in the safe cast operator
as? is that it never throws an exception on failure but returns null instead.

Smart Casts
With smart casts, the Kotlin compiler helps you avoid redundant casts by doing them for you.
Kotlin does this whenever the compiler can infer stricter type constraints, including smart-casting
from nullable to non-nullable types. This is demonstrated in Listing 4.31. For this, assume that
Visibilities 91

Component defines the function component(), Composite has an additional function compos-
ite(), and Leaf has a function leaf().

Listing 4.31 Smart Casts


val comp: Component? = Leaf() // Type is nullable ‘Component?’

if (comp != null) { comp.component() } // Smart-cast to Component


if (comp is Leaf) { comp.leaf() } // Smart-cast to Leaf
when (comp) {
is Composite -> comp.composite() // Smart-cast to Component
is Leaf -> comp.leaf() // Smart-cast to Leaf
}
if (comp is Composite && comp.composite() == 16) {} // Smart-cast to Composite
if (comp !is Leaf || comp.leaf() == 43) {} // Smart-cast to Leaf inside condition
if (comp !is Composite) return
comp.composite() // Smart-cast to Composite (because of return above)

The Kotlin compiler can infer stricter type constraints in many situations, helping you focus on
your actual logic. This includes casting to non-nullable types, as in the first check above. It also
includes casts to subtypes, which is enabled using the is operator in if and when expressions.
Casting inside the conditions is possible because Kotlin evaluates non-atomic conditions lazily.
This means that in conjunctions (composed with &&), the right-hand side will not be evaluated
if the left-hand side is already false. Similar logic applies to disjunctions (composed with ||).
Lastly, the compiler is intelligent enough to infer when a previous check would have led to a
return, as in the last example. If comp were not of type Composite, the last line wouldn’t be
reached, thus it can be smart-cast there as well.

Note
Smart casts can only be applied if the variable cannot change between the type check (or null
check) and its usage. This is another reason to prefer val over var and to apply the principles
of information hiding in object-oriented code to limit variable manipulations from the outside.
You can recognize smart-casts in IntelliJ and Android Studio 3 by their green highlighting.

Visibilities
A crucial principle in OO is information hiding, meaning the internal implementation details of
a class shouldn’t be visible to the outside. Instead, only a well-defined interface is exposed that
makes it more predictable in which ways the class is used and mutated. Visibilities are the facilita-
tors of information hiding. They allow you to define what’s accessible from where.
92 Chapter 4 Object Orientation in Kotlin

Declarations in Classes or Interfaces


First, let’s consider class or interface members (properties and methods). For these, there are four
visibility modifiers (three of which work the same way as in Java). In roughly ascending order of
restrictiveness, they are
 public: The member is accessible wherever its containing class is visible. This is used for
the members that form the well-defined interface of the class or interface.
 internal: The member is accessible anywhere inside the same module, given that the
containing class is visible. This is used for parts of the well-defined interface that are only
relevant for clients from the same module.
 protected: The member is visible inside the class itself and its child classes. This is useful
for members that shouldn’t be accessible to the outside but are useful to implement child
classes. Note that this only makes sense for abstract and open classes.
 private: The member is accessible only inside its containing class. This is used heavily to
hide class internals from the outside.

Java Note
Note that there is no package-private visibility as in Java. The default visibility in Kotlin is
public, and the closest thing to package-private is internal.

The internal visibility refers to modules. So what’s meant by module here? A module is a set of
Kotlin files that are compiled together, for instance an Android Studio module, a Maven module,
a Gradle source set, or a set of files compiled in one Ant task.

Listing 4.32 demonstrates the effect of all visibility modifiers.

Listing 4.32 Visibility Modifiers


open class Parent {
val a = "public"
internal val b = "internal"
protected val c = "protected"
private val d = "private"

inner class Inner {


val accessible = "$a, $b, $c, $d" // All accessible
}
}

class Child : Parent() {


val accessible = "$a, $b, $c" // d not accessible because private
}
Visibilities 93

class Unrelated {
val p = Parent()
val accessible = "${p.a}, ${p.b}" // p.c, p.d not accessible
}

Inner classes can access even private members, whereas child classes (from the same module) can
access all but private members. Unrelated classes can access public members, plus internal ones if
the unrelated class is in the same module.

To add visibility modifiers to constructors, getters, and setters, you must add the corresponding
soft keywords (constructor, get, set) explicitly, as shown in Listing 4.33.

Listing 4.33 Setting the Visibility of Constructors, Getters, and Setters


open class Cache private constructor() {
val INSTANCE = Cache()

protected var size: Long = 4096 // Getter inherits visibility from property
private set // Setter can have a different visibility
}

To attach a nondefault visibility to the primary constructor, the constructor keyword is


required. Secondary constructors can simply be prefixed with visibility modifiers as they are. The
visibility of getters is always defined by the visibility of the property itself, thus there’s no addi-
tional line saying protected get. By default, the setter inherits the property’s visibility as well,
but this can be changed by adding the set keyword together with the desired visibility. Because
there’s no custom setter implementation here, writing private set suffices. Note that you
cannot use primary constructor properties if you want to set an explicit setter visibility.

Note
One more thing to consider with visibilities is that inline methods cannot access class proper-
ties with a more restrictive visibility. For instance, a public inlined method cannot access an
internal property because that property may not be accessible at the inlined position.

Top-Level Declarations
Kotlin allows top-level (or file-level) declarations of properties, functions, types, and objects. So
how can you restrict the visibilities of these?

For top-level declarations, only three of the visibility modifiers are allowed because protected
doesn’t make sense here. Other than that, the visibilities work conceptually the same way, except
there’s no containing class that further restricts the visibility.
94 Chapter 4 Object Orientation in Kotlin

 public: The declaration is accessible everywhere.


 internal: The declaration is accessible everywhere inside the same module.
 private: The declaration is accessible only within its containing file.

Data Classes
With data classes, Kotlin provides a convenient way to implement classes with the main purpose
of holding data. Typical operations on data objects are reading, altering, comparing, and copying
the data. All of these are particularly easy with Kotlin’s data classes.

Using Data Classes


Declaring a data class is as simple as prefixing the class declaration with the data modifier as in
Listing 4.34.

Listing 4.34 Declaring a Data Class


data class Contact(val name: String, val phone: String, var favorite: Boolean)

This way, the compiler automatically generates several useful methods that can be derived from
just the data, namely hashCode and equals, toString, a copy method, and componentN
methods (component1, component2, …), one for each property of the data class. Listing 4.35
shows how these can be used.

Listing 4.35 Generated Members of Data Classes


val john = Contact("John", "202-555-0123", true)
val john2 = Contact("John", "202-555-0123", true)
val jack = Contact("Jack", "202-555-0789", false)

// toString
println(jack) // Contact(name=Jack, phone=202-555-0789, favorite=false)

// equals
println(john == john2) // true
println(john == jack) // false

// hashCode
val contacts = hashSetOf(john, jack, john2)
println(contacts.size) // 2 (no duplicates in sets, uses hashCode)
Data Classes 95

// componentN
val (name, phone, _) = john // Uses destructuring declaration
println("$name's number is $phone") // John’s number is 202-555-0123

// copy
val johnsSister = john.copy(name = "Joanne")
println(johnsSister) // Contact(name=Joanne, phone=202-555-0123, favorite=true)

The compiler-generated implementations of hashCode, equals, and toString work as you’d


expect. Without the data modifier, toString would print the memory address, john would be
unequal john2, and contacts.size would be 3. Remember that the double-equal operator in
Kotlin calls equals to check for structural equality.

Up to this point, data classes essentially remove the burden of generating those implementa-
tions in your IDE. But that only takes a few seconds, so the main benefit here is not the time
you save but readability and consistency. Can you quickly tell whether that IDE-generated equals
method contains a manual change? Are equals and hashCode still up to date, or did someone
forget to regenerate them after changing a property? With data classes, you don’t have these
problems.

Additionally, the componentN functions enable so-called destructuring declarations as shown in


Listing 4.35. These allow you to easily extract all values stored inside an object of a data class into
separate variables. Note that you can use an underscore to ignore values you don’t need.

Lastly, the copy method allows you to copy a data object while modifying any of its properties,
using named parameters. So you could just as well change the phone number or the favorite indi-
cator without changing any of the other properties.

Note
Inside the data class, you can still declare custom implementations for hashCode, equals,
and toString. If you do, the compiler won’t generate the corresponding method.
However, componentN and copy methods with signatures that conflict with the generated ones
cannot be defined inside the data class. This ensures they work as expected.

When working with data classes, there are several limitations you should be aware of.

 Data classes cannot be inherited from. In other words, they cannot be open or abstract.
 They cannot be sealed or inner classes (sealed classes are discussed later).
 The primary constructor needs at least one parameter in order to generate the methods.
 All parameters in the primary constructor must be upgraded to properties using val or var.
You may also add secondary constructors to data classes.
96 Chapter 4 Object Orientation in Kotlin

Inheritance with Data Classes


Data classes cannot be inherited from. However, they may have a superclass and implement inter-
faces. In case you make use of this, there are several things to consider.
 If the parent class contains open implementations of hashCode, equals, or toString, the
compiler will generate implementations that override those.
 If the parent class contains final implementations of hashCode, equals, or toString, the
implementations from the parent class are used.
 The parent class may contain methods called componentN as long they’re open and
their return types match the data class. Here, the compiler will again generate overriding
implementations for the data class. Parent implementations that are final or don’t have a
matching signature will cause an error at compile time.
 Similarly, the parent class must not have an implementation of copy that matches the
signature it would have in the data class.
 The latter two rules make sure that destructuring declarations and copying always work as
expected.

Lastly, you cannot inherit between data classes. The reason for this is there’s no way to generate
the required methods in a consistent and correct way for such hierarchies. I won’t show a listing
for all this because I’d recommend you avoid using general inheritance with data classes. A legiti-
mate special case, which covers sealed classes, is shown in the next section.

Tip
Data classes are useful to define containers for multiple values whenever a function should
conceptually return multiple values. If a function for instance returns a name and a password,
you can encapsulate that as follows:
data class Credentials(val name: String, val password: String)

Then you can use Credentials as the return type.


Kotlin also has the predefined data classes Pair and Triple to carry two or three values,
respectively. However, those are less expressive than a dedicated data class.

Enumerations
For enumerations, Kotlin provides enum classes. Listing 4.36 shows the simplest form of using an
enum class. They’re useful to model a finite set of distinct objects, such as states of a system, direc-
tions, or a set of colors.

Listing 4.36 Declaring a Simple Enum Class


enum class PaymentStatus {
OPEN, PAID
}
Enumerations 97

The enum modifier transforms a class declaration into an enumeration class, similar to inner
and data class modifiers. Enum constants are separated by commas and are objects at runtime,
meaning there can only ever be one instance of each.

Although this already covers the most frequent use cases of enums, you can also attach construc-
tor parameters, properties, and methods. This is demonstrated in Listing 4.37.

Listing 4.37 Declaring an Enum Class with Members


enum class PaymentStatus(val billable: Boolean) { // Enum with a property

OPEN(true) {
override fun calculate() { ... }
},
PAID(false) {
override fun calculate() { ... }
}; // Note the semicolon: it separates the enum instances from the members

fun print() { println("Payment is ${this.name}") } // Concrete method


abstract fun calculate() // Abstract method
}

What’s important to note here is that the enum instances must be defined first, followed by a
semicolon to separate any members that follow. You can add any properties or methods to the
class. Abstract methods must be overridden in each enum instance. Listing 4.38 shows how to use
your own as well as compiler-generated members of enums.

Listing 4.38 Working with Enums


val status = PaymentStatus.PAID
status.print() // Prints "Payment is PAID"
status.calculate()

// Kotlin generates ‘name’ and ‘ordinal’ properties


println(status.name) // PAID
println(status.ordinal) // 1
println(status.billable) // false

// Enum instances implement Comparable (order = order of declaration)


println(PaymentStatus.PAID > PaymentStatus.OPEN) // true
98 Chapter 4 Object Orientation in Kotlin

// ‘values’ gets all possible enum values


var values = PaymentStatus.values()
println(values.joinToString()) // OPEN, PAID

// ‘valueOf’ retrieves an enum instance by name


println(PaymentStatus.valueOf("OPEN")) // OPEN

For each enum instance, Kotlin generates a name property that holds the instance name as a
string. The ordinal property is simply the zero-indexed order in which enum instances are
declared inside the enum class. All enum instances implement Comparable by comparing the
ordinal values.

Custom properties and members can be accessed as usual. Additionally, Kotlin generates the
method valueOf to get an enum instance by name, and values to get all possible instances.
There are also generic variants of these two called enumValueOf and enumValues. If there’s no
enum instance with the given name, an IllegalArgumentException is thrown.

Since enums have a fixed set of possible instances, the compiler can infer if a when expression that
uses the enum is exhaustive, as shown in Listing 4.39.

Listing 4.39 Exhaustive when Expression with Enum


val status = PaymentStatus.OPEN

val message = when(status) {


PaymentStatus.PAID -> "Thanks for your payment!"
PaymentStatus.OPEN -> "Please pay your bill so we can buy coffee."
} // No else branch necessary

Sealed Classes
Sealed classes are used to build restricted class hierarchies, in the sense that all direct subtypes of
the sealed class must be defined inside the same file as the sealed class itself. Listing 4.40 shows a
sealed class that represents binary trees. It must be declared in a .kt file, not a script.

Listing 4.40 Declaring a Sealed Class with Subtypes


sealed class BinaryTree // Sealed class with two direct subclasses (and no others)
data class Leaf(val value: Int) : BinaryTree()
data class Branch(val left: BinaryTree, val right: BinaryTree) : BinaryTree()

// Creates a binary tree object


val tree: BinaryTree = Branch(Branch(Leaf(1), Branch(Leaf(2), Leaf(3))), Leaf(4))
Sealed Classes 99

To declare a sealed class, simply prefix the class declaration with the sealed modifier.
Remember that the class body with curly braces is optional if it’s empty. The sealed class
itself is implicitly abstract (thus cannot be instantiated) and its constructor is private (thus
the class cannot be inherited from in another file). This already implies that all direct
subtypes must be declared inside the same file. However, subtypes of subtypes may be declared
anywhere. You could add abstract or concrete members to the sealed class, just like in other
abstract classes. Data classes are useful to define the subtypes of sealed classes, as shown in
Listing 4.40.

Note
You can think of sealed classes as a generalization of enum classes. They have the additional
possibility to instantiate as many objects of each subtype as you want. In contrast, enums
always have single instances such as PAID and OPEN.

The benefit of sealed classes is that you have more control over your class hierarchy, and the
compiler uses this as well. Because all direct child classes are known at compile time, the compiler
can check when expressions for exhaustiveness. Hence, you can omit the else branch whenever
you cover all cases, as with enums in Listing 4.39. This is demonstrated in Listing 4.41 with the
example of a sealed class representing expressions.

Listing 4.41 Exhaustive when Expression with Sealed Class


sealed class Expression // Sealed class representing possible arithmetic expressions
data class Const (val value: Int) : Expression()
data class Plus (val left: Expression, val right: Expression) : Expression()
data class Minus (val left: Expression, val right: Expression) : Expression()
data class Times (val left: Expression, val right: Expression) : Expression()
data class Divide(val left: Expression, val right: Expression) : Expression()

fun evaluate(expr: Expression): Double = when(expr) {


is Const -> expr.value.toDouble()
is Plus -> evaluate(expr.left) + evaluate(expr.right)
is Minus -> evaluate(expr.left) - evaluate(expr.right)
is Times -> evaluate(expr.left) * evaluate(expr.right)
is Divide -> evaluate(expr.left) / evaluate(expr.right)
} // No else branch required: all possible cases are handled

val formula: Expression = Times(Plus(Const(2), Const(4)), Minus(Const(8), Const(1)))


println(evaluate(formula)) // 42.0
100 Chapter 4 Object Orientation in Kotlin

Here, the value of the when expression is used as the return value of evaluate. Since all cases
are covered, no else branch is required and there’s still always a well-defined return value. Note
that subtypes of subtypes don’t interfere with the exhaustiveness of the type check because
all sub-subtypes are subsumed in their parent subtype. Also remember that Kotlin smart-casts
expr on the right-hand sides of the when expression, making either value or left and right
accessible.

Regarding control over the hierarchy, sealed classes implement a strict form of the closedness
principle. Without this, an interface should be frozen once it’s public because there could be any
number of child classes in different projects. Changing or removing a method would crash all of
them. With sealed classes, new methods can be added easily because there can be no new direct
child classes. On the downside, adding subtypes to sealed classes involves effort for adjusting all
when expressions that use no else branch. You have to balance between how likely subtypes are
to change versus how likely their operations are to change.

On Algebraic Data Types


For the mathematically inclined reader, I want to mention that data classes and sealed
classes are Kotlin’s way to implement algebraic data types, more specifically, sum and
product types.
These concepts originate from functional programming and type theory. You can think of prod-
uct types as records or tuples of multiple elements (or a struct in C/C++). These can be
implemented using data classes:

interface A { val a: Int }


interface B { fun b() }
data class Both(override val a: Int) : A, B { override fun b() { ... } }

To instantiate an object of such a product type, you need values for each of its constituent
types. Here, B doesn’t have properties so only a is required. If there are x possible values for
type A and y possible values of type B, their product type has x*y possible values—thus the
name algebraic, and in this case product.
Sum types, on the other hand, are used to construct types that can contain either one of their
constituent types. This is what is expressed with sealed classes; a BinaryTree is either a
Leaf or a Branch. The more general sum type of two types A and B is often aptly named
Either:

sealed class Either


class MyA(override val a: Int) : Either(), A
class MyB : Either(), B { override fun b() { ... } }

Here, any object of type Either either conforms to the interface A or B, but not both.
This is why sealed classes and data classes often work well together, producing a sum type
consisting of product types, such as the BinaryTree.
Objects and Companions 101

Objects and Companions


Most of the time, you create objects as instances of classes. In Kotlin, they can be declared directly
as well. The first way to do this is to use object expressions, which allow you to create an object on
the fly and use it as an expression. Listing 4.42 shows the simplest case of an ad-hoc object that
holds some data.

Listing 4.42 Simple Object Expressions


fun areaOfEllipse(vertex: Double, covertex: Double): Double {
val ellipse = object { // Ad-hoc object
val x = vertex
val y = covertex
}
return Math.PI * ellipse.x * ellipse.y
}

Kotlin uses the object keyword to create objects. For simple objects, such as Listing 4.42, the
keyword is directly followed by the object body. However, object expressions are more powerful
and allow creating an object of a class while overriding certain members but without defining a
whole new subclass. For this, objects can inherit from a class and any number of interfaces, just
like classes. Shown in Listing 4.43, listeners are a common use case.

Java Note
Object expressions as shown in Listing 4.43 supersede Java’s anonymous inner classes.
As a side note, object expressions can access nonfinal variables from their enclosing scope, in
contrast to anonymous inner classes in Java.

Listing 4.43 Object Expressions with Supertype


scrollView.setOnDragListener(object : View.OnDragListener {
override fun onDrag(view: View, event: DragEvent): Boolean {
Log.d(TAG, “Dragging...”)
return true
}
})

To create an object with supertypes, the object keyword is now followed by a colon and its
supertypes, just as you do for classes. Note that OnDragListener is an interface, therefore it is
not followed by parentheses because there’s no constructor to call.
102 Chapter 4 Object Orientation in Kotlin

Tip
Java interfaces with just a single abstract method (called SAM interfaces) such as
OnDragListener are more easily constructed using a lambda expression:
scrollView.setOnDragListener { view, event -> ... }

Objects can be considered a generalization of this and can be used even when you want to
implement multiple interfaces or methods, not just one.

Object expressions are only usable as types within local and private declarations. If you return
an object expression from a public method, its return type is Any because callers cannot see the
object expression. You can mitigate this problem by using an interface that makes the object’s
structure available to the outside. Listing 4.44 demonstrates this behavior.

Listing 4.44 Object Expressions as Return Type


class ReturnsObjectExpressions {
fun prop() = object { // Returns an object; infers Any type
val prop = "Not accessible"
}

fun propWithInterface() = object : HasProp { // Returns an object; infers HasProp


override val prop = "Accessible"
}

fun access() {
prop().prop // Compile-time error (Any does not have prop)
propWithInterface().prop // Now possible (HasProp has prop)
}
}

interface HasProp { // Allows exposing the ‘prop’ to the outside


val prop: String
}

Basically, as long as you’re using object expressions locally, Kotlin makes sure you can access
the object’s members. If you return an object expression from a nonprivate method, make sure
to implement an interface that can be used as the return type. Otherwise, the return value is
hardly useful.
Objects and Companions 103

Object Declarations as Singletons


Used judiciously, singletons are a useful pattern for software development. If you think about
it, singletons are just objects—meaning there’s only one instance at runtime. Thus, you use the
object keyword to create singletons in Kotlin, as in Listing 4.45.

Listing 4.45 Object Declaration for a Registry Singleton


import javafx.scene.Scene

object SceneRegistry { // Declares an object: this is effectively a Singleton


lateinit private var homeScene: Scene
lateinit private var settingsScene: Scene
fun buildHomeScene() { … }
fun buildSettingsScene() { … }
}

Unlike object expressions, this object is no longer anonymous because the object keyword is
followed by the object’s name. This is called an object declaration. It’s not an expression and
cannot be used on the right-hand side of an assignment. Members of an object are accessed by
simply qualifying them with the object name as in Listing 4.46.

Listing 4.46 Accessing Object Members


val home = SceneRegistry.buildHomeScene()
val settings = SceneRegistry.buildSettingsScene()

Object declarations can use inheritance as usual. Also, they can be nested into (non-inner) classes
or other object declarations, but they cannot be local (declared inside a function). In contrast,
object expressions can be used everywhere.

Note
Object declarations are initialized lazily, when they’re first accessed. Hence, if the object is
never used at runtime, it will use zero resources. This is a common way to implement single-
tons manually as well.
In contrast to this, object expressions are evaluated and initialized eagerly right where they’re
declared. This makes sense because their value is typically used directly.

Companion Objects
Object declarations inside classes can be made companion objects. Members of such companion
objects are then accessible by only prefixing their containing class, and without creating an object
of that class. Listing 4.47 demonstrates this.
104 Chapter 4 Object Orientation in Kotlin

Listing 4.47 A Factory as Companion Object


data class Car(val model: String, val maxSpeed: Int) {
companion object Factory {
fun defaultCar() = Car("SuperCar XY", 360)
}
}

val car = Car.defaultCar() // Calls companion method: same as Car.Factory.defaultCar()

Declaring a companion object is as simple as prepending the object declaration with the compan-
ion modifier. You can give it an explicit name, such as Factory in Listing 4.47; otherwise it gets
the default name Companion. All members of the companion object can be accessed directly on
the Car class, or alternately by prefixing the companion name explicitly.

You may have wondered about a static modifier in Kotlin already—there is none. Instead of
static, Kotlin uses companion objects and top-level declarations. While companion objects
better resemble static declarations from other languages, top-level declarations may be the
more idiomatic way to migrate certain static functions to Kotlin. Companions are useful for
methods closely related to the containing class, such as factory methods. Note that, in contrast to
static members in other languages, the companion members are not accessible on an instance of
the containing class because this would be bad practice anyway.

Also, even though accessing companion members looks like accessing static members in other
languages, companion objects really are objects at runtime. Thus, they can inherit a class and
implement interfaces, as in Listing 4.48.

Listing 4.48 Companion Object with Factory Interface


interface CarFactory {
fun defaultCar(): Car
}

data class Car(val model: String, val maxSpeed: Int) {


companion object Factory : CarFactory { // Companions can implement interfaces
override fun defaultCar() = Car("SuperCar XY", 360)
}
}

All supertypes are appended after a colon, and you now use the override modifier inside the
companion. Regarding interoperability, the way defaultCar is declared in Listing 4.48, you can
only call this from Java as Car.Factory.defaultCar(). You can tell the compiler to compile
this to actual static members in the Java bytecode using the @JvmStatic annotation, as in
Generics 105

@JvmStatic override fun defaultCar(). That way, you can call it directly on the class
from Java, using Car.defaultCar(). Interoperability issues and annotations are covered in
detail in Chapter 5, Interoperability with Java.

Note
You can define extension functions on companions as on any other objects, which can then be
called from Kotlin in the same way as all other companion members:

fun Car.Factory.cheapCar() = Car("CheapCar 2000", 110)


val cheap = Car.cheapCar()

Generics
Generic programming is a powerful way to avoid code duplication. The idea is to lift type-
specific algorithms and data structures to more generic ones. Like so often in software develop-
ment, this is a process of abstraction. You focus on the fundamental properties required for the
algorithm or data structure to work and abstract them from a specific type. When Musser and
Stepanov introduced the concept in 1989, they described it as follows: “Generic programming
centers around the idea of abstracting from concrete, efficient algorithms to obtain generic
algorithms that can be combined with different data representations to produce a wide variety
of useful software.”5

Generics as you know them from most object-oriented languages and as they’re discussed in this
section are just one genericity mechanism to achieve this. Implementing algorithms in a parent
class is also generic programming per the definition above. However, this section covers the
specific genericity mechanism called generics.

Generic Classes and Functions


As mentioned, the main idea is to generalize data structures and algorithms. For data struc-
tures, you use generic classes. These typically implement methods, which are then generic.
Additionally, you can declare generic functions outside of generic classes to define generic algo-
rithms. With this, the two main entities you want to generalize are covered. Prominent examples
of generic data structures are collection types such as List<E> and Map<K, V>—where E, K, and
V are called generic type parameters (or just type parameters).

5. David R. Messer and Alexander A. Stepanov, “Generic programming.” Presented at the First
International Joint Conference of International Symposium on Symbolic and Algebraic
Computation (ISSAC)-88 and Applied Algebra, Algebraic Algorithms, and Error Correcting Codes
(AAECC)-6, Rome, Italy, July 4–8, 1988. Lecture Notes in Computer Science, P. Gianni. 358. Springer-
Verlag, 1989, pp. 13–25. (http://stepanovpapers.com/genprog.pdf)
106 Chapter 4 Object Orientation in Kotlin

Note
Regarding the term type parameter, think about what a parameter fundamentally is. As in math-
ematical functions, parameters introduce degrees of freedom. Imagine for instance a family of
functions f(x; p) = p * x^2 with parameter p. We call f parameterized, and can instanti-
ate a family of similar but different functions from it by changing p. All share certain properties
such as f(-x) = f(x), but not others such as f(1) = 1.
The same applies to generic types like Set<E>, where the type parameter E introduces a
degree of freedom—the type of the elements. The concrete type is not fixed when declaring the
class but only when instantiating it. Still, all sets share certain properties.

Generic Classes
To declare a generic class in Kotlin, you define all its generic type parameters in angle brackets
directly after the class name, as shown in Listing 4.49.

Listing 4.49 Declaring and Instantiating a Generic Class


class Box<T>(elem: T) { // Generic class: can be a box of integers, box of strings,

val element: T = elem
}

val box: Box<Int> = Box(17) // Type Box<Int> can also be inferred


println(box.element) // 17

The declaration of the Box type uses one type parameter T, which is appended to the class name in
angle brackets. This parameter can then be used just like any other type inside the class to define
property types, return types, and so forth. At runtime, it will be the concrete type used when instan-
tiating a Box object, here Int. Thus, the constructor accepts an Int and element has type Int as
well. If you want more than one type parameter, comma-separate them inside the angle brackets.

Tip
You don’t have to give the name T to the type parameter, but the convention is to use single
uppercase letters that carry semantic meaning. Most frequent are the following:
T = type
E = element
K = key
V = value
R = return type
N = number
Generics 107

In Listing 4.49, notice how the concrete type of Box you want to have is only defined when
instantiating a Box, not when declaring it. That’s when you use your degree of freedom to use
a specific Box variant. Without this, you’d have to repeat all code inside Box for every type you
want to support, such as IntBox or PersonBox. You may think about just creating an AnyBox,
but that stops being type-safe once you actually want to do something with it. The advantage of
generics is that they are type-safe.

To get you accustomed to generics, Listing 4.50 provides a more complex example that generalizes
the BinaryTree data structure from the section on sealed classes. With this implementation, you
can create binary trees that contain any kind of value in their leaves.

Listing 4.50 Generalizing the BinaryTree Class


sealed class BinaryTree<T> // Now generic: can carry values of any type
data class Leaf<T>(val value: T) : BinaryTree<T>()
data class Branch<T>(
val left: BinaryTree<T>,
val right: BinaryTree<T>
) : BinaryTree<T>()

val tree: BinaryTree<Double> = Leaf(3.1415) // Uses a binary tree with Double values

Type Aliases
A useful feature in combination with generic classes, but also function types, are type aliases.
These allow you to provide a more meaningful name to existing types, for instance:

typealias Deck = Stack<Card> // New name for generics-induced type


typealias Condition<T> = (T) -> Boolean // New name for function type

At use-site, Kotlin inlines the aliased type so that you can use the type alias as you would use
the underlying type:
val isNonNegative: Condition<Int> = { it >= 0 } // Can assign a lambda

Note that Kotlin infers the type of the it variable based on the definition of the type alias.

Generic Functions
Generic functions allow genericity on the algorithm side, even outside generic classes. They are
defined and used as in Listing 4.51.
108 Chapter 4 Object Orientation in Kotlin

Listing 4.51 Declaring and Using a Generic Function


fun <T> myListOf(vararg elements: T) = Arrays.asList(*elements) // * is spread operator

val list0: List<Int> = myListOf<Int>(1, 2, 3) // All types explicit, no type inference


val list1: List<Int> = myListOf(1, 2, 3) // Infers myListOf<Int>
val list2 = myListOf(1, 2, 3) // Infers MutableList<Int> and myListOf<Int>
val list3 = myListOf<Number>(1, 2, 3) // Infers MutableList<Number>

// Without parameters
val list4 = myListOf<Int>() // Infers MutableList<Int>
val list5: List<Int> = myListOf() // Infers myListOf<Int>

This function is similar to Kotlin’s built-in listOf and helps constructing lists. To make
it a generic function, the fun keyword is followed by the generic type parameters in angle
brackets. These can be used like any other type inside the function body. Like the built-in
listOf function, you can call it with or without explicit type arguments—the compiler can
infer the type based on the arguments. If you want to use a different type (such as Number)
or don’t pass in any arguments, you have to either declare the variable type explicitly, as
in val list: List<Number>, or declare the function type parameters explicitly, as in
myListOf<Number>.

Note
We say that generics support compile-time polymorphism, specifically parametric polymor-
phism. This means that a generic function behaves uniformly for all concrete type
arguments. This is in contrast to runtime polymorphism via inheritance and method
overloading.

Reification
In generic functions, you sometimes want to access the generic type parameter. A common
example is to check whether an object is a subtype of the type parameter. However, type
information for generic type parameters is not preserved at runtime so that you cannot
check subtypes using is as you normally would. This is called type erasure and is a limitation
of the JVM that forces you to use reflection in these cases. Reflection means introspecting
(and potentially even modifying) your own code at runtime, for instance, examining the type
of an object at runtime for which no static type information is available (due to type erasure).
Listing 4.52 illustrates an example with a generic function that filters an iterable object by a
given type.
Generics 109

Listing 4.52 A Generic Function that Accesses Its Type Parameter


fun <T> Iterable<*>.filterByType(clazz: Class<T>): List<T?> {
@Suppress("UNCHECKED_CAST") // Must suppress unchecked cast
return this.filter { clazz.isInstance(it) }.map { it as T? } // Uses reflection
}

val elements = listOf(4, 5.6, "hello", 6, "hi", null, 1)


println(elements.filterByType(Int::class.javaObjectType)) // 4, 6, 1

This function filters any iterable object to only the types that are assignable to the given type T.
But to do so, isInstance must use reflection to check whether an element is an instance of T.
Also, you have to suppress unchecked cast warnings, and calling the function could be easier.

Luckily, there is a “trick” in Kotlin called reification. Reification allows you to access generic type
information—but only in inline functions. Because for these, the concrete type argument is
inlined into the call site, allowing you to make it available at runtime. Listing 4.53 provides a
better implementation of the function from Listing 4.52.

Listing 4.53 A Generic Function Using Reification


inline fun <reified T> Iterable<*>.filterByType(): List<T?> { // Inline + reified
return this.filter { it is T }.map { it as T? } // No reflection; can use ‘is’
}

val elements = listOf(4, 5.6, "hello", 6, "hi", null, 1)


println(elements.filterByType<Int>()) // 4, 6, 1

This is now an inline function, and the type parameter T has the reified modifier, which allows
you to now use the is operator with it. Also, there are no warnings to suppress, and the call site
is concise. The way this works is that all occurrences of T in the inline function are replaced with
the concrete type argument. Concretely, the function call from Listing 4.53 is effectively trans-
formed to the function call in Listing 4.54.

Listing 4.54 Inlined Code of Generic Function


elements.filter { it is Int }.map { it as Int? } // Inlined function and inserted T

In short, reification is useful if you need access to a generic type parameter at runtime but is avail-
able only for inline functions—you cannot generally prevent type erasure on the JVM.
110 Chapter 4 Object Orientation in Kotlin

Covariance and Contravariance


This section first introduces the concepts of types versus classes, and then moves on to variance,
more specifically covariance and contravariance.

Java Note
So far, generics in Kotlin work the same way as in Java. But diving deeper into generics and
variance now, you’ll see several ways in which Kotlin improves on Java.

First, you have to differentiate between classes and types. Consider, for instance, Kotlin’s Int
class. This is also a type. But what about the nullable Int? then? This is not a class: Nowhere do
you declare a class Int?. But it’s still a type, the type of nullable integers. Similarly, List<Int> is
not a class but a type; List is a class. The same holds for all generic classes—concrete instantia-
tion induces a new type. This is what makes generics so powerful. Table 4.1 gives more examples of
classes versus types.

Table 4.1 Classes Versus Types


Entity Is It a Class? Is It a Type?
Task Yes Yes
Task? No Yes
List<Task> No Yes
List Yes Yes
(Task) -> Unit No Yes

Tip
Think about it like this: Classes and types are in a subset relation, meaning that the set of all
classes is a subset of the set of all types. In other words, every class is also a type, but not
every type is also a class.

Now that you know the difference between classes and types, let’s explore the concept of vari-
ance, starting with covariance.

Covariance
Intuitively, covariance means that a subtype can be used in place of one of its supertypes. You’re
used to this from variable assignments, where you can assign an object of a subclass to a variable
of a superclass, as in Listing 4.55.
Generics 111

Listing 4.55 Covariance of Subclasses in Assignments


val n: Number = 3 // Integer is a subclass of Number
val todo: Archivable = Task("Write book", 99) // Task is a subclass of Archivable

Beside assignments, return types also allow the use of variance, as shown in Listing 4.56.

Listing 4.56 Covariance of Return Types


abstract class Human(val birthday: LocalDate) {
abstract fun age(): Number // Specifies that method returns a Number
}

class Student(birthday: LocalDate) : Human(birthday) {


// Return types allow variance so that Int can be used in place of Number
override fun age(): Int = ChronoUnit.YEARS.between(birthday, LocalDate.now()).toInt()
}

So far, this is completely intuitive because type safety is preserved even if you use a subclass in
these places.

Java Note
In Java, arrays are covariant, meaning Student[] is a subtype of Human[], but this is not
type-safe. It can cause an ArrayStoreException at runtime:

Number[] arr = new Integer[3]; // Arrays are covariant


arr[0] = 3.7; // Causes ArrayStoreException

In Kotlin, arrays are not covariant and therefore type-safe. Covariance of arrays would allow you
to instantiate an array of integers and assign it to an array of numbers. Adding elements to that
array would then not be type-safe because you could add any number to that array, which doesn’t
fit into the integer array that is actually used. This is illustrated in Listing 4.57.

Listing 4.57 Invariance of Arrays in Kotlin (Type-Safe)


val arr: Array<Number> = arrayOf<Int>(1, 2, 3) // Compile-time error
arr[0] = 3.1415 // Would be unsafe (but cannot happen)

The same potential problem applies to collection types and generic types in general. That’s why,
for instance, Java’s collections such as List<E> and Set<E> (which are mutable in Java) are not
covariant to preserve type safety, as demonstrated in Listing 4.58.
112 Chapter 4 Object Orientation in Kotlin

Listing 4.58 Invariance of Java Collections (Type-Safe)


List<Number> numbers = new ArrayList<Integer>(); // Compile-time error

We say that List<E> is not covariant with respect to its type parameter E (in Java). And as you know
now, it would be unsafe if it was because it would have the same problem shown above for arrays.
So why does Listing 4.59 compile just fine in Kotlin?

Listing 4.59 Variance of Read-Only Collections in Kotlin (Type-Safe)


val numbers: List<Number> = listOf<Int>(1, 2, 3) // Works

This is in fact type-safe in Kotlin. To see why, remember that Kotlin clearly differentiates between
mutable and read-only collections in its standard library (in contrast to Java). Also, recall that the
problem with type safety stemmed from adding new elements to an array or list. Generally, covari-
ance of generic types with respect to a type parameter T is type-safe as long as objects of type T are
never consumed but only produced. Thus, covariance is safe for the read-only list above but no longer
safe for mutable collections, which is why those are not covariant in Kotlin, as shown in Listing 4.60.

Listing 4.60 Invariance of Mutable Collections in Kotlin


val numbers: MutableList<Number> = mutableListOf<Int>(1, 2, 3) // Compile-time error

If this was possible, you could again produce an error at runtime simply by calling numbers.
add(3.7), which would try to add a double into a list of integers. In other words, in
MutableList<T>, the type parameter T is consumed by methods such as add(t: T) and there-
fore not only produced. This makes covariance unsafe.

As an intuition, we said that covariance means you can use a subtype in place of a supertype.
More formally, it means that subtype relations are preserved when wrapping with a covariant
type. Assume you have a covariant type Covariant<T>, and a type Child with its parent type
Parent. Covariance implies that Covariant<Child> is still a subtype of Covariant<Parent>,
just like List<Int> is a subtype of List<Number> because Kotlin’s List<T> is covariant.

Contravariance
Contravariance is the counterpart to covariance and intuitively means that, in place of a subtype,
a supertype can be used. This may be less intuitive at first glance, but the following subsection
demonstrates some intuitive use cases. While you can think of covariant types as producers (they
cannot consume T, only produce T), you can think of contravariant types as consumers. The
following subsection introduces several examples of contravariant types.

Formally, contravariance means that subtype relationships are reversed when wrapping
with a contravariant type. If you have a contravariant type Contravariant<T>, then
Contravariant<Parent> becomes a subtype of Contravariant<Child>. For example, a
Comparable<Number> is a Comparable<Int>. This makes sense because an object that knows
how to compare numbers also knows how to compare integers. So the intuition is not formally
correct here, and you are in fact still using subtypes in place of supertypes.
Generics 113

Declaration-Site Variance
Kotlin introduces declaration-site variance as a language concept so that variance becomes part of
a class’ contract. This way, the class behaves as a co- or contravariant type at all use sites. For this
purpose, Kotlin uses the in and out modifiers. Listing 4.61 demonstrates how to declare a covari-
ant, and therefore read-only, stack class.

Listing 4.61 Covariant Stack Class


open class Stack<out E>(vararg elements: E) { // ‘out’ indicates covariance
protected open val elements: List<E> = elements.toList() // E used in out-position
fun peek(): E = elements.last() // E used in out-position
fun size() = elements.size
}

val stack: Stack<Number> = Stack<Int>(1, 2, 3) // Allowed because covariant

This defines a read-only stack type. As you know now, read-only access is essential to be able to
make this type covariant. Covariance with respect to the type parameter E is indicated by <out
E>, so that E can only appear only in out-position—and indeed, it only appears as return type in
the peek function. Note that, in the elements type List<E>, E appears in covariant position
as well because List<E> is covariant. Using MutableList<E> would cause a compile-time error
because E would appear in invariant position there.

Due to <out E>, it’s now part of the contract of the Stack class that it only produces objects
of type E but never consumes them. However, you can see that covariance comes at a cost—you
cannot define methods in the Stack class that add or remove elements. For this, you’ll need a
mutable subclass, like in the predefined collection classes. Listing 4.62 gives an example.

Listing 4.62 An Invariant Mutable Stack Class


class MutableStack<E>(vararg elements: E) : Stack<E>() { // Mutable: must be invariant
override val elements: MutableList<E> = elements.toMutableList()
fun push(element: E) = elements.add(element) // E used in in-position
fun pop() = elements.removeAt(elements.size - 1) // E used in out-position
}

val mutable: MutableStack<Number> = MutableStack<Int>(1, 2, 3) // Compile-time error

Because a mutable stack is no longer purely a producer and uses E at in-positions (as parameter of
push and in MutableList<E>), it cannot have the out modifier. Hence, MutableStack<Int> is
not a subtype of MutableStack<Number>. Note that the stack implementations here are
very simplistic and don’t handle exceptions such as empty stacks.
114 Chapter 4 Object Orientation in Kotlin

Conversely, you can declare a type as being contravariant by using the in modifier, as in
Listing 4.63.

Listing 4.63 A Contravariant Compare<T> Type


interface Compare<in T> { // ‘in’ indicates contravariance
fun compare(a: T, b: T): Int // T is only used at in-position
}

The Compare type is now contravariant with respect to its type parameter. Intuitively, this means
you can use a more general comparator to compare more specific objects. Consider Listing 4.64 as
an example and recall that Task is a subclass of Issue.

Listing 4.64 Using Contravariance of Compare<T>


val taskComparator: Compare<Task> = object : Compare<Issue> { // Uses contravariance
override fun compare(a: Issue, b: Issue) = a.priority - b.priority
}

This code uses an object expression to initialize an object of type Compare<Issue> and assigns
it to a variable of type Compare<Task>. Contravariance makes sense here because an object that
can compare issues in general can also compare tasks. Notice that you can think of Compare (or
comparators) as consumers because they only consume objects of type T to compare them but
never produce objects of type T. To familiarize yourself with contravariance, Listing 4.65 presents
another example.

Listing 4.65 Another Contravariant Type Repair<T>


interface Repair<in T> { // Contravariant
fun repair(t: T) // T in in-position
}

open class Vehicle(var damaged: Boolean)


class Bike(damaged: Boolean) : Vehicle(damaged)

class AllroundRepair : Repair<Vehicle> { // Knows how to repair any vehicle


override fun repair(vehicle: Vehicle) {
vehicle.damaged = false
}
}

val bikeRepair: Repair<Bike> = AllroundRepair() // Uses contravariance


Generics 115

Here, Bike is a subtype of Vehicle and the interface Repair<in T> indicates that an object of
that type is able to repair T objects. Hence, AllroundRepair is able to repair vehicles of all kinds,
in particular bikes. This is a typical scenario of contravariance, and again it makes sense that you
can assign bikeRepair to an object that implements Repair<Vehicle>; because it can repair
any vehicle, it can also repair bikes.

With declaration-site variance, the implementor of a type has to think about whether
the type can support variance already when declaring it. This takes the burden from the
user of the type who would otherwise have to think about variance at the use site. This
results in more readable code. However, not every type can support co- or contravariance,
as you’ve seen with mutable collections. This only works in the special cases where
objects of a type always act purely as a producer (covariant) or a consumer
(contravariant).

If this isn’t the case, Kotlin still supports use-site variance as well. This is useful if a type is not
strictly a producer or consumer, but you’re using it like one in a part of your code. Then, in that
part of your code, the type can be co- or contravariant.

Java Note
Java supports only use-site variance and no declaration-site variance, but there is a JEP for it.6

Table 4.2 summarizes the distinction between covariant, contravariant, and invariant types.

Table 4.2 An Overview of Variance


Covariance Contravariance Invariance
Role Producer Consumer Producer and
Consumer
Example Kotlin’s List<T> Repair<T> MutableList<T>
Keyword out in -

Finally, Figure 4.1 shows the effect of variance on the subtype relationships in Kotlin’s
type system. This diagram only shows non-nullable types but the same relationships exist
for their corresponding nullable types. Note that Nothing really is the absolute subtype
of all other types while Any is still a subtype of the absolute supertype Any? that is not
shown here.

6. http://openjdk.java.net/jeps/300
116 Chapter 4 Object Orientation in Kotlin

Any A B
A is subtype of B

Number List<Number> Repair<Int>

Int List<Int> Repair<Number>

Nothing

Figure 4.1 Kotlin-type system in the light of variance

Use-Site Variance
For use-site variance, Kotlin has type projections. A projection is simply a restriction of a
type Type<T> to a subset of itself. For instance, by projecting Type<T> to its covariant subset
Type<out T>, it becomes restricted to members that don’t consume T. Effectively, Type<out
T> is a producer. Conversely, projection to Type<in T> restricts the type to those members that
don’t produce T, thus making it a consumer.

Java Note
Java supports only use-site variance and uses the syntax List<? extends T> for covariance
and List<? super T> for contravariance. These are called wildcard types. You may be famil-
iar with the “PECS” mnemonic that Joshua Bloch introduced to better remember these in Java;
it stands for Producer-extends-Consumer-super. In Kotlin, it’s a lot easier thanks to the more
expressively named modifiers in and out: Producer-out-Consumer-in.

Listing 4.66 gives an example of use-site covariance in assignments.

Listing 4.66 Use-Site Covariance in Assignments


val normal: MutableList<Number> = mutableListOf<Int>(1, 2, 3) // Compile-time error
val producer: MutableList<out Number> = mutableListOf<Int>(1, 2, 3) // Now covariant

// Can only read from producer, not write to it


producer.add(???) // Parameter type is Nothing, so impossible to call ‘add’
val n: Number = producer[0] // Returns Number
Generics 117

Although MutableList is not covariant by itself, you can state at use-site that you want to
use it as a producer using MutableList<out Number>. This way, consumer methods are no
longer callable because, wherever the type parameter T was used as a method parameter, the type
becomes Nothing. And because there is no valid object of type Nothing, it is impossible to call
methods such as add or remove. For the contravariant case, Listing 4.67 gives an example.

Listing 4.67 Use-Site Contravariance in Assignments


val normal: MutableList<Int> = mutableListOf<Number>(1.7, 2f, 3) // Compile-time error
val consumer: MutableList<in Int> = mutableListOf<Number>(1.7, 2f, 3) // Contravariant

// Can write to consumer normally, but only read values as Any?


consumer.add(4)
val value: Any? = consumer[0] // Return type is Any? (only type-safe choice)

Here, the mutable list is used as a consumer, a contravariant type, using <in Int>. Wherever T
appears as a parameter in the generic class definition, it becomes Int. Thus, add accepts values of
type Int. More important, T in out-position becomes Any? because whatever value is read from
the list, it can be stored in a variable of type Any?. Hence, it’s not strictly a consumer. But reading
data from it is discouraged by the fact that it can only return values with type Any?.

Note the contrary nature of how T is mapped in the co- and contravariant cases. For covariance,
to prevent writing to the list, T is transformed to Nothing, the absolute bottom of the type hier-
archy in Kotlin. For contravariance, it’s transformed to Any?, the absolute supertype in the type
hierarchy.

More commonly, use-site variance is used to increase flexibility of methods. Imagine you want to
implement a method to move all elements from one mutable stack onto another mutable stack.
Listing 4.68 shows a naïve implementation along with its problems.

Listing 4.68 Function Without Use-Site Variance


fun <E> transfer(from: MutableStack<E>, to: MutableStack<E>) {
while (from.size() > 0) { to.push(from.pop()) }
}

val from = MutableStack<Int>(1, 2, 3)


val to = MutableStack<Number>(9.87, 42, 1.23)
transfer(from, to) // Compile-time error, although it would be safe

Even though it would be safe to transfer all elements from the stack of integers to the stack of
numbers, this is not guaranteed in the contract of the transfer function. Since MutableStack is
invariant, you might as well be pushing elements onto the from stack or reading elements from
the to stack. Use-site variance solves this problem, as in Listing 4.69.
118 Chapter 4 Object Orientation in Kotlin

Listing 4.69 Use-Site Variance Increases Function Flexibility


fun <E> transfer(from: MutableStack<out E>, to: MutableStack<in E>) {
// Same as before
}
// …
transfer(from, to) // Works now due to variance

Note that covariance does not imply immutability in any way; even though you cannot call
methods like add or remove on a MutableList<out Int>, you could still call a method such as
clear, which removes all elements. This is also the case in Listing 4.69. In fact, the from stack is
emptied as a side effect of the transfer function, due to the calls to the pop function.

Tip
Prefer declaration-site variance to use-site variance when the class allows it, meaning when it’s
a producer or consumer. This simplifies code at the use site and allows for increased flexibility
without additional syntax.

Bounded Type Parameters


Sometimes, you want to restrict the possible concrete types that can be used for a generic type
parameter. For instance, the interface Repair<in T> may only make sense for type parameters
that are a subclass of Vehicle. That way, users of the class cannot instantiate a Repair<Person>
or a Repair<Int>. Listing 4.70 does this by bounding the type parameter.

Listing 4.70 Bounded Type Parameter


interface Repair<in T : Vehicle> { // Upper bound for T is Vehicle
fun repair(t: T)
}

The only thing that changed here is the upper bound of T in <in T : Vehicle>. Like a super-
type, the upper bound is specified after a colon. If you want to define multiple upper bounds for
the same type parameter, you must use a where clause as in Listing 4.71. For this example, let’s
assume there are vehicles that are not repairable so you have a separate interface Repairable
that is not implemented by Vehicle.

Listing 4.71 Multi-Bounded Type Parameter


interface Repairable { … }
interface Repair<in T>
where T : Vehicle, // where clause lists all type bounds
T : Repairable {
fun repair(t: T)
}
Generics 119

Here, the upper bounds are pulled out of the angle brackets and defined in the where clause
instead. Note that each upper bound is separated by a comma, and the class body comes after the
where clause. Also, by convention, the where clause is indented by twice the normal indentation
and the upper bounds are aligned at the same indentation level.

Tip
You can use where clauses whenever you define a type parameter, not only when there are
multiples for the same type. In particular, you can use it in generic methods to avoid repeating
an upper bound for multiple parameters:
fun <T> min(a: T, b: T): T where T : Comparable<T> = if (a < b) a else b

Note that you can also inline the where clause if it’s sufficiently short. Otherwise, you should
favor making the code less dense and splitting it up into multiple lines.

Upper bounds induce a stricter contract on the objects of type T. In other words, you know more
about the objects and can therefore do more with them. For example, inside the repair(t: T)
method in the interface Repair from Listing 4.72, you get access to all members of Vehicle
as well as Repairable on t because you know that t: T fulfills the contracts of both these
types.

Star Projections
Sometimes, you don’t need to know anything about the concrete type of a type parameter. Still,
you want your code to be type-safe. In such cases, you can use Kotlin’s star projections. Listing 4.72
shows an example of a function that prints all elements of an array.

Listing 4.72 Using Star Projections


fun printAll(array: Array<*>) { // Nothing known about type of array, but type-safe
array.forEach(::println)
}

printAll(arrayOf(1, 2, 3))
printAll(arrayOf("a", 2.7, Person("Sandy"))) // Can pass in arrays of arbitrary type

Here, you don’t care about the concrete type parameter of Array, and star projections let
you express that as Array<*>. Note that you could also define a generic function fun <E>
printAll(array: Array<E>), but there’s no need for this function to be generic. You could
even call the generic version in the same way because the compiler automatically infers that
you’re calling printAll<Int>(…) or printAll<Any>(…).

When working with star projections, the compiler differentiates between three cases when access-
ing them. You should understand what it does and how to work with it.
120 Chapter 4 Object Orientation in Kotlin

1. For covariant types Producer<out T> that you can only read from, Producer<*>
becomes Producer<out Any?> because that’s the only type-safe choice if T is unknown
and unrestricted.

However, if T has an upper bound Upper as in Producer<out T: Upper>, it becomes


Producer<out Upper> because all read elements can be safely cast to Upper.

2. For contravariant types, Consumer<*> becomes Consumer<in Nothing> because there’s


no way to safely pass an element of type T if T is unknown. This is easy to see because
nothing stops you from actually instantiating a Consumer<Nothing>, making it in fact
impossible to pass any element of type T.

3. Lastly, an invariant type Inv<T> combines the two cases above. For read access, Inv<*>
becomes Inv<out Any?> (or Inv<out Upper> if T has an upper bound). Again, that’s
the only type-safe choice for an unknown T. Conversely, for write access, Inv<*> becomes
Inv<in Nothing> as it’s impossible to safely pass an element of an unknown type T.

Listing 4.73 exemplifies the first two cases. It defines an interface with one contravariant and one
covariant type parameter, and it shows how they behave with star projections.

Listing 4.73 Star Projections for Variant Types


interface Function<in T, out R> { // Contravariant w.r.t. T and covariant w.r.t. R
fun apply(t: T): R
}

object IssueToInt : Function<Issue, Int> {


override fun apply(t: Issue) = t.priority
}

// Star projections
val f1: Function<*, Int> = IssueToInt // apply() not callable but would return Int
val f2: Function<Task, *> = IssueToInt // apply() callable with Task, returns Any?
val f3: Function<*, *> = IssueToInt // apply() not callable and would return Any?

val errand = Task("Save the world", 80)


val urgency: Any? = f2.apply(errand)

For the following, note that apply is a consumer of T and a producer of R. Using a star projection
on the contravariant type parameter T transforms apply(t: T) to apply(t: Nothing) so
that it becomes impossible to call this method. You could still call methods that don’t have T as a
parameter if there were any.
Summary 121

Conversely, projecting the covariant type parameter R doesn’t prevent you from calling any
methods but makes all methods with return type R return Any?, resulting in apply(t: T):
Any?. This is why, when calling f2 in the last line, urgency does not have type Int. This is intui-
tive because you can read the declaration f2: Function<Task, *> as “a function that takes in
a task and returns a value of unknown type.”

Lastly, projecting both type parameters declares a function taking in some unknown type and
returning an unknown type. This combines the two restrictions above; you can no longer call
methods that consume T, and all methods that return R can only safely return Any?.

An example for the third case, star projection of an invariant type, was already given in Listing 4.72:
Inside printAll, you cannot call array.set because its signature is array.set(index: Int,
value: Nothing). Reading from the array returns values of type Any?. Hence, the restrictions at
work are similar to Function<*, *> except that there is only one type parameter.

In summary, when you use star projections, the compiler ensures type safety by projecting types
appropriately where necessary.

Summary
As an object-oriented language, Kotlin lets you compose systems based on classes and objects
that encompass data as properties and behavior as methods. Such object-oriented systems should
follow the principle of information hiding to keep up maintainability and reduce coupling
between classes. Visibilities are the primary way to implement information hiding.

Combining Kotlin’s object-oriented concepts with functional programming features offers power-
ful ways to write more concise code, solve common tasks more easily, and increase reusability. For
example, nonfixed parts of the logic in classes may be passed as lambda expressions. Consider the
Strategy pattern, a well-known design pattern for object-oriented systems. It can be implemented
more easily by using lambdas that define specific strategies.

You now know how to declare and use object-oriented entities in Kotlin. Here’s an overview of
the main entities along with a differentiation of which is used in which kind of situation.

1. Use normal classes for general entities in your system.

2. Use interfaces to define the most high-level or abstract contracts for your types.

3. Use abstract classes for non-instantiable classes that are not directly entities in the system
but encapsulate common logic of multiple subclasses.

4. Use inheritance (judiciously) to avoid code duplication and to ideally implement only the
differences between more specialized subclasses.

5. Use data classes for types carrying mainly or only data.

6. Use sealed classes to implement restricted hierarchies (or algebraic types).

7. Use enum classes to define finite sets of distinct values.


122 Chapter 4 Object Orientation in Kotlin

8. Use object expressions to create ad-hoc objects or an implementation of one or more


interfaces that is used only once.

9. Use object declarations to create singletons or companion objects.

10. Use generic classes if the logic doesn’t depend on the specific type of elements, most
commonly in data structures.

Apart from these object-oriented entities, Kotlin provides a variety of convenient language
features that help solve common development tasks. These features include delegated properties
to reuse common accessor logic, extension functions to easily enhance existing APIs, declaration-
site variance to avoid duplicate code at the call site, and many more. All these can greatly improve
developer productivity and code quality.
5
Interoperability with Java

Synergy is better than my way or


your way. It’s our way.
Stephen Covey

Interoperability has been one of Kotlin’s priorities from its inception. In this chapter, you’ll learn
the ins and outs of mixing Kotlin and Java, how their language concepts map to each other, what
happens under the hood in the Kotlin compiler to aid interoperability, and how you can write
code that further facilitates it.

Using Java Code from Kotlin


Java code can be called from Kotlin in the way you would expect most of the time. In this section,
we’ll explore the details and possible pitfalls, as well as how to write Kotlin-friendly Java code and
how to improve your Java code to provide additional information for the Kotlin compiler, espe-
cially about nullability.

First, it’s important to appreciate the fact that you can easily use the Java standard library and any
third-party library in a natural way. This is demonstrated in Listing 5.1.

Listing 5.1 Using Java Libraries from Kotlin


import com.google.common.math.Stats
import java.util.ArrayList

// Using the standard library


val arrayList = ArrayList<String>() // Uses java.util.ArrayList
arrayList.addAll(arrayOf("Mercury", "Venus", "Jupiter"))
arrayList[2] = arrayList[0] // Indexed access operator calls ‘get’ and ‘set’
124 Chapter 5 Interoperability with Java

// Looping as usual, ArrayList provides iterator()


for (item in arrayList) { println(item) }

// Using a third-party library (Google Guava)


val stats = Stats.of(4, 8, 15, 16, 23, 42)
println(stats.sum()) // 108.0

Concepts that differ in Java are mapped to Kotlin automatically. For instance, the indexed access
operator for collections is translated to get and set calls under the hood. Also, you can still iterate
anything that provides an iterator method using a for loop. This is the easiest and most preva-
lent use case: simply using Java from Kotlin seamlessly without even thinking about it. With this
in mind, let’s now explore special cases to be able to handle mixed-language projects confidently.

Note
What’s critical to Kotlin is that you can use any existing Java libraries, opening up a plethora
of powerful code to reuse. This includes the standard library and any third-party libraries and
frameworks, whether it be Spring,1 JUnit,2 or the Android Software Development Kit (SDK).

Calling Getters and Setters


In Kotlin, you don’t explicitly call getSomething or setSomething but instead use the property
syntax that calls the getter and setter under the hood. You want to keep it that way when using
Java field accessors for consistency and brevity, and it’s possible by default. Listing 5.2 gives an
example.

Listing 5.2 Calling Java Getters and Setters


// Java
public class GettersAndSetters {
private String readOnly = "Only getter defined";
private String writeOnly = "Only setter defined";
private String readWrite = "Both defined";

public String getReadOnly() { return readOnly; }


public void setWriteOnly(String writeOnly) { this.writeOnly = writeOnly; }
public String getReadWrite() { return readWrite; }
public void setReadWrite(String readWrite) { this.readWrite = readWrite; }
}

1. https://spring.io/
2. https://junit.org/
Using Java Code from Kotlin 125

// Kotlin
private val gs = GettersAndSetters()
println(gs.readOnly) // Read-only attribute acts like a val property

gs.readWrite = "I have both" // Read-write attribute acts like a var property
println(gs.readWrite)

gs.setWriteOnly("No getter") // Write-only properties not supported in Kotlin

This automatic conversion to a Kotlin property works as long as a Java class provides a parameter-
less method starting with “get” and optionally a single-parameter method starting with “set”. It
also works for Boolean expressions where the getter starts with “is” instead of “get”, but there’s
currently no such mechanism if they start with “has” or other prefixes.

Write-only properties are currently not supported in Kotlin, which is why setWriteOnly cannot
be called with property syntax as writeOnly.

Handling Nullability
Regarding nullability, there’s a gap in expressiveness between Kotlin and Java that has to be
handled: in Kotlin, every variable is either nullable or not, in Java there’s no nullability informa-
tion expressed in the language itself because every variable may potentially be null (except prim-
itive types). However, handling all data coming from Java as nullable in Kotlin would introduce
unnecessary code complexity due to null handling. To handle this problem, Kotlin uses so-called
platform types for data coming from Java.

Nullables and Mapped Types


As mentioned in Chapter 2, Diving into Kotlin, primitive types are mapped to the corresponding
(non-nullable) Kotlin types. For example, int is mapped to kotlin.Int, boolean to kotlin.
Boolean, and vice versa. This makes sense because primitive types cannot be null in Java.
However, boxed types must be mapped to nullable types, for instance java.lang.Integer to
kotlin.Int?, java.lang.Character to kotlin.Char?, and vice versa.

Generally, interoperability with Java introduces the problems of mapping any objects coming
from Java to a Kotlin type. Obviously, it’s not possible to map them all to non-nullable types
because that would be unsafe. But because you want to exclude nullability from your code as
much as possible, Kotlin doesn’t simply make them all nullable either. Kotlin supports nullabil-
ity annotations from JSR 305,3 JetBrains, Android, FindBugs,4 and more that define annotations
such as @Nullable and @NotNull. This additional information allows a better mapping to
appropriate Kotlin types.

3. https://jcp.org/en/jsr/detail?id=305
4. http://findbugs.sourceforge.net/
126 Chapter 5 Interoperability with Java

Platform Types
Ultimately, the Kotlin team decided to leave the nullability decision to the developer in cases
where nullability cannot be inferred. This is represented using platform types such as SomeType!.
Here, SomeType! means either SomeType or SomeType?. These are the cases where you can
choose to store an object of that platform in a nullable or non-nullable variable. See Table 5.1 for
several examples of mapped types.

Table 5.1 Mapped Types and Nullability


Java Declaration Kotlin Type Explanation
String name; kotlin.String! May or may not be null

@NotNull String name; kotlin.String Supposed to not be null

@Nullable String name; kotlin.String? Can be null

@NotNull String name = null; kotlin.String Source of null pointer exception


even when using Kotlin

Java objects without annotation may or may not be null, so Kotlin will map them to the corre-
sponding platform type, allowing you to treat it as either nullable or non-nullable. If annotations
are used, Kotlin uses the additional knowledge about nullability. However, objects marked as
@NotNull in Java that still contain null by mistake can cause a null pointer exception in Kotlin.

Note
You cannot explicitly create platform types yourself. Types such as String! are not valid syn-
tax in Kotlin and are only used by the compiler and IDE in order to communicate types to you.

So platform types denote that the associated type may or may not be nullable, and the developer
is able to decide whether the platform type should be handled as nullable or not by using the
corresponding type; this is shown in Listing 5.3.

Listing 5.3 Handling Platform Types in Kotlin


// Java
public static String hello() { return "I could be null"; }

// Kotlin
val str = hello() // Inferred type: String (by default)
println(str.length) // 15

val nullable: String? = hello() // Explicit type: String?


println(nullable?.length) // 15
Using Java Code from Kotlin 127

Without an explicit type, the compiler infers the non-nullable variant of the platform type so
that you can use it without handling null. This is unsafe if you don’t think about whether the
method you’re calling does actually never return null. Hence, I recommend stating the type
explicitly when calling a Java method. That way, your intention and the fact that you thought
about nullability are explicit.

You’ll quickly come across more complex platform types as well. These come from mapped types and
generic types that may or may not carry nullability information. Consider the examples in Table 5.2.

Table 5.2 Examples of Platform Types (Combined with Mapped Types)


Java Return Type Platform Type Explanation
String String! May or may not be null, thus either
String or String?

List<String> (Mutable)List Maybe mutable and maybe nullable


<String!>! list, containing maybe nullable
Strings

Map<Int, String> (Mutable)Map<Int!, Maybe mutable and maybe nullable


String!>! map, containing maybe nullable Ints
as keys and maybe nullable Strings
as values

Object[] Array<(out) Any!>! Maybe nullable array, containing


maybe nullable Anys or maybe sub-
types of it (because arrays are covari-
ant in Java)

long[] LongArray! Maybe nullable array of primitive


longs (the whole array may be null)

List<? super Integer> MutableList Maybe nullable and mutable list, con-
<in Int!>! taining Ints or parent types of it

The first row demonstrates that unannotated types are translated to platform types by default.
Next, a List<T> coming from Java may be used as a mutable list or not, and may itself again
be nullable or not (like any other type). Additionally, elements of the list are also potentially
nullable. The same holds for other generic types and their type arguments, as shown in the third
row. There, the generic type arguments Int and String are mapped to their platform type coun-
terparts. This is necessary because Java allows null as both key and value.

Regarding the fourth row, an array coming from Java is also potentially nullable. On top of that, it
may contain subtypes of the declared type argument because arrays are covariant in Java so that an
Object[] may contain subtypes of Object. This is why it is translated to a potentially out-projected
type. Note also that java.lang.Object is translated to kotlin.Any as defined per the mapped
types. A complete list of mapped types is available in the language documentation.5 Additionally,

5. https://kotlinlang.org/docs/reference/java-interop.html#mapped-types
128 Chapter 5 Interoperability with Java

arrays of primitive types are mapped to their corresponding specialized Kotlin class, such as
IntArray, DoubleArray, and CharArray. Lastly, a contravariant list coming from Java becomes
an in-projected type in Kotlin. As you can see, it even becomes a definitely mutable list because it
wouldn’t be usable otherwise as an in-projected list. However, you shouldn’t use a wildcard type as a
return type in Java because it makes the return type harder to work with for the caller.

Note
Kotlin not only maps primitive arrays to their corresponding mapped types (IntArray,
LongArray, CharArray, and so forth) and vice versa, it also incurs no performance cost
compared to Java when working with these.
For instance, reading a value from an IntArray does not actually call get, and writing a value
does not generate a call to set. Also, iteration and in checks generate no iterator or other
objects. The same holds for all mapped types of primitive arrays.
Recall that such arrays can be created in Kotlin directly using dedicated helper methods
intArrayOf, doubleArrayOf, longArrayOf, and so on.

Adding Nullability Annotations


Handling platform types is only required if there’s no nullability information the compiler
can use. Even though the Java language itself doesn’t have a concept for this, you can actually
attach nullability info via annotations such @NotNull and @Nullable. Such annotations are
already fairly widespread, and there are various libraries containing such annotations. The Kotlin
compiler can currently process the following nullability annotations.
 The Android annotations @NonNull and @Nullable from the package
android.support.annotations. These can be used out of the box on Android.
 The JetBrains annotations @NotNull and @Nullable from org.jetbrains.
annotations that are used for static analysis in Android Studio and IntelliJ IDEA
and can also be used out of the box in both.
 Annotations from the javax.annotation package.
 FindBugs annotations from the package edu.umd.cs.findbugs.annotations.
 The Lombok annotation lombok.NonNull.6
 Eclipse’s7 nullability annotations from org.eclipse.jdt.annotation.

With these, Kotlin supports the most widely used annotations. Listing 5.4 provides an example
for how to use JetBrains’ annotations; the others work the same way. If you’re using others, such
as NetBeans8 or Spring Framework annotations, you’ll have to transition to one of the above to
get better type inference in Kotlin.

6. https://projectlombok.org/features/NonNull
7. http://www.eclipse.org/
8. https://netbeans.org/
Using Java Code from Kotlin 129

Listing 5.4 Using Nullability Annotations


// Java
public static @Nullable String nullable() { return null; }
public static @NotNull String nonNull() { return "Could be null, but with warning"; }

// Kotlin
val s1 = nullable() // Inferred type: String?
val s2 = nonNull() // Inferred type: String

Note that nothing stops you from returning null from the nonNull method in Java, you’ll only
receive a warning in AndroidStudio and IntelliJ, depending on which nullability annotations
you use. For instance, the JetBrains annotations are used for static analysis in Android Studio and
IntelliJ so that such code would yield a warning.

In this way, nullability annotations allow you to get more type information in Kotlin. In other
words, they disambiguate the impreciseness of Type! to either Type or Type?. Let’s consider the
examples in Table 5.3 and contrast them to the above table of platform types.

Table 5.3 Examples of Inferred Types (When Using Nullability Annotations)


Java Return Type Inferred Kotlin Type Explanation
@NonNull String String Non-nullable String

@Nullable String String? Nullable String

@NonNull List (Mutable)List Non-nullable but maybe mutable list,


<String> <String!> containing maybe nullable Strings

List<@NonNull String> (Mutable)List Maybe nullable and maybe mutable


<String>! list, containing non-nullable Strings

@NonNull (Mutable)List Non-nullable but maybe mutable list,


<String> containing non-nullable Strings
List<@NonNull String>

In short, by resolving the ambiguity to either @NonNull or @Nullable, the return type of the
Java method is mapped accordingly when called from Kotlin. Note that, to use annotations on
generic types argument as in List<@NonNull String>, you need to use an implementation of
JSR 3089 such as the checker framework.10 The purpose of JSR 308 is to allow annotating any type
in Java, including generic type parameters, and it was incorporated into Java Standard Edition 8.

9. https://jcp.org/en/jsr/detail?id=308
10. https://checkerframework.org/
130 Chapter 5 Interoperability with Java

Escaping Clashing Java Identifiers


When mapping between languages, there is always a disparity between the keywords defined
in the languages. Kotlin has several keywords that don’t exist in Java and are thus valid Java
identifiers, such as val, var, object, and fun. All these must be escaped using backticks in
Kotlin as shown in Listing 5.5. This is done automatically in IntelliJ and Android Studio.

Listing 5.5 Handling Name Clashes


// Java
class KeywordsAsIdentifiers {
public int val = 100;
public Object object = new Object();
public boolean in(List<Integer> list) { return true; }
public void fun() { System.out.println("This is fun."); }
}

// Kotlin
val kai = KeywordsAsIdentifiers()
kai.`val`
kai.`object`
kai.`in`(listOf(1, 2, 3))
kai.`fun`()

Even though it looks inconvenient, it’s not something you have to think about explicitly thanks
to the IDE. It still does clutter up the code a little but should only appear in rare cases anyway.
If you have to escape frequently, you may want to rethink your naming standards in Java—none
of the above identifiers convey much meaning when used as identifiers.

Calling Variable-Argument Methods


Calling vararg methods defined in Java works naturally so that you can pass in an arbitrary
number of arguments. However, in contrast to Java, you cannot pass an array to a vararg method
directly. Instead, you have to use the spread operator by prefixing a * to the array. Listing 5.6 shows
both ways to call a variable-argument method.

Listing 5.6 Calling a Variable-Argument Method


// Java
public static List<String> myListOf(String... strings) { // Vararg method from Java
return Arrays.asList(strings); // Passes in vararg unchanged
}
Using Java Code from Kotlin 131

// Kotlin
val list = myListOf("a", "b", "c") // Can pass in any number of args
val values = arrayOf("d", "e", "f")
val list2 = myListOf(*values) // Spread operator required

Using Operators
You can call Java methods like operators in Kotlin if they have the right signature. For instance,
defining a plus or minus method in a class allows you to add or subtract objects of it. Listing 5.7
provides an example.

Listing 5.7 Using Java Methods as Operators


// Java
public class Box {
private final int value;

public Box(int value) { this.value = value; }


public Box plus(Box other) { return new Box(this.value + other.value); }
public Box minus(Box other) { return new Box(this.value - other.value); }
public int getValue() { return value; }
}

// Kotlin
val value1 = Box(19)
val value2 = Box(37)
val value3 = Box(14)

val result = value1 + value2 - value3 // Uses ‘plus’ and ‘minus’ as operators
println(result.value) // 42

Although you can write Java code that targets the predefined set of operators, you cannot define
other methods that allow infix notation in Kotlin.

Using SAM Types


Interfaces with a single abstract method (“SAM types”) can be called from Kotlin without boiler-
plate thanks to SAM conversions. Listing 5.8 shows how a lambda expression can be used to imple-
ment the single abstract method of a SAM interface.
132 Chapter 5 Interoperability with Java

Listing 5.8 Using SAM Types from Kotlin


// Java
interface Producer<T> { // SAM interface (single abstract method)
T produce();
}

// Kotlin
private val creator = Producer { Box(9000) } // Inferred type: Producer<Box>

In the Java code, this defines a SAM type, with produce being the single abstract method. To
create a Producer<T> in Kotlin, you don’t need to use an object expression (or an anonymous
inner class as in Java) but can instead use a lambda expression that provides the implementa-
tion of the single abstract method. In Listing 5.8, you have to add Producer in front of the
lambda expression, otherwise the type of the right-hand side would be () -> Box and not
Producer<Box>. This is not necessary when passing in a function argument, as shown in
Listing 5.9, because the compiler can then infer the type.

Listing 5.9 SAM Conversion in Function Arguments


// Kotlin
val thread = Thread { // No need to write Thread(Runnable { … })
println("I'm the runnable")
}

The Thread constructor accepts a Runnable, which is instantiated here using SAM conversions.
The code is equivalent to Thread(Runnable { … }). But because the constructor argument is
of type Runnable, you’re not required to add an additional type hint in front of the lambda here.
Consequently, you can use Kotlin’s convention to move lambdas out of the parentheses and then
omit the parentheses because the lambda is the only parameter.

This mechanism is extremely useful for many built-in SAM types such as Comparator, Runnable,
and many listener classes.

Further Interoperability Considerations


You’ve now seen that you can call Java code from Kotlin in a natural way most of the time.
Sometimes, as with SAM conversions, it’s even more convenient than from Java itself. The main
point to keep in mind is nullability and how it maps to Kotlin. Other considerations to keep in
mind can be explained briefly.
 Methods that throw a checked exception in Java can be called from Kotlin without having
to handle the exception because Kotlin decided against checked exceptions.
Using Kotlin Code from Java 133

 You can retrieve the Java class of an object as SomeClass::class.java or instance.


javaClass.
 You can use reflection on Java classes from Kotlin and use a reference to the Java class as
the entry point. For instance, Box::class.java.declaredMethods returns the methods
declared in the Box class.
 Inheritance works naturally across Kotlin and Java; both support only one superclass but
any number of implemented interfaces.

Since Kotlin was designed with interoperability in mind, it works seamlessly with Java most of
the time. The following section explores what you should keep in mind when calling Kotlin code
from Java.

Using Kotlin Code from Java


The other way around, Kotlin code can also be called from Java. The best way to understand how
to access certain elements, such as extension functions or top-level declarations, is to explore how
Kotlin translates to Java bytecode. An additional benefit of this is that you get more insights into
the inner workings of Kotlin.

Accessing Properties
Before diving into the details, remember that when I say “field,” I’m usually referring to Java
(unless I’m explicitly referring to Kotlin’s backing fields). Contrarily, when talking about “proper-
ties,” I’m referring to Kotlin because they don’t directly exist in Java.

As you know, you don’t need to implement property getters and setters manually in Kotlin.
They’re created automatically and can be accessed from Java as you would expect, as shown in
Listing 5.10.

Listing 5.10 Calling Getters and Setters


// Kotlin
class KotlinClass {
val fixed: String = "base.KotlinClass"
var mutable: Boolean = false
}

// Java
KotlinClass kotlinClass = new KotlinClass();
String s = kotlinClass.getFixed(); // Uses getter of ‘val’
kotlinClass.setMutable(true); // Uses setter of ‘var’
boolean b = kotlinClass.getMutable(); // Uses getter of ‘var’
134 Chapter 5 Interoperability with Java

Notice that Boolean getters also use the prefix get by default instead of is or has. However, if the
property name itself starts with is, the property name is used as the getter name—and not just
for Boolean expressions; this is irrespective of the property type. Thus, calling the Boolean prop-
erty isMutable instead would result in the getter of the same name isMutable. Again, there’s
currently no such mechanism for properties starting with has but you can always define your
own JVM name by annotating the getter or setter with @JvmName, as in Listing 5.11.

Listing 5.11 Custom Method Name Using @JvmName


// Kotlin
class KotlinClass {
var mutable: Boolean = false
@JvmName("isMutable") get // Specifies custom getter name for Java bytecode
}

// Java
boolean b = kotlinClass.isMutable(); // Now getter is accessible as ‘isMutable’

Tip
One extremely useful feature in IntelliJ IDEA and Android Studio is the ability to see the
compiled bytecode of your Kotlin code and then the decompiled Java code. For this, press
Ctrl+Shift+A (Cmd+Shift+A on Mac) to invoke the action search, then type “Show Kotlin
Bytecode” (or “skb”) and press Enter.
Inside the panel that opens, you see the generated Kotlin bytecode and you can click the
Decompile button to see the decompiled Java code.11 For the simple class from Listing 5.10
without annotation, you’ll see this result (plus some metadata):

public final class KotlinClass {


@NotNull private final String fixed = "base.KotlinClass";
private boolean mutable;

@NotNull public final String getFixed() {


return this.fixed;
}
public final boolean getMutable() { return this.mutable; }
public final void setMutable(boolean var1) { this.mutable = var1; }
}

11. If the Decompile button doesn’t appear, make sure the Java Bytecode Decompiler plugin is
enabled.
Using Kotlin Code from Java 135

Note that the class is final in Java because it’s non-open, that the nonprimitive String field
has a @NotNull annotation to carry the nullability information to Java, and that the read-only
property fixed is final as well. Lastly, note the amount of boilerplate that can be avoided due
to Kotlin’s syntax (val and var), its choice of defaults (closed classes and non-nullables), and
compiler-generated code (getters and setters).

Exposing Properties as Fields


As you’ve learned, properties are compiled to a private field with getter and setter by default (this
is also the case for file-level properties). However, you can use @JvmField to expose a property
directly as a field in Java, meaning the field inherits the visibility of the Kotlin property and no
getter or setter is generated. Listing 5.12 demonstrates the difference.

Listing 5.12 Exposing a Property as a Field using @JvmField


// Kotlin
val prop = "Default: private field + getter/setter" // Here no setter because read-only

@JvmField
val exposed = "Exposed as a field in Java" // No getter or setter generated

// Decompiled Java code (surrounding class omitted)


@NotNull private static final String prop = "Default: private field + getter/setter";
@NotNull public static final String getProp() { return prop; }

@NotNull public static final String exposed = "Exposed as a field in Java";

As you can see, the property annotated with @JvmField is compiled to a public field that can be
accessed directly in Java. This works the exact same way for properties inside a class; by default,
they are also accessible via getters and setters but can be exposed as fields.

Note that there are several restrictions for using the @JvmField annotation, and it’s a good exer-
cise to think about why these restrictions exist.

 The annotated property must have a backing field. Otherwise, there would be no field to
expose in the Java bytecode.
 The property cannot be private because that makes the annotation superfluous. For
private properties, no getters or setters are generated anyway because there would be no
value to them.
 The property cannot have the const modifier. Such a property becomes a static final field
with the property’s visibility anyway, so @JvmField would have no effect.
136 Chapter 5 Interoperability with Java

 It cannot have an open or override modifier. This way, field visibilities and existence
of getters and setters is consistent between superclass and subclasses. Otherwise, you
could accidentally hide the superclass field in Java with a field that has a more restrictive
visibility. This can lead to unexpected behavior and is a bad practice.
 A lateinit property is always exposed so that it can be initialized from anywhere it’s
accessible, without assumptions about how it’s initialized. This is useful when an external
framework initializes the property. @JvmField would be superfluous here as well.
 It cannot be a delegated property. Delegation only works with getters and setters that can
be routed to the delegate’s getValue and setValue methods, respectively.

Using File-Level Declarations


In Kotlin, you can declare properties and functions on the file level. Java doesn’t support this,
so these are compiled to a class that contains the properties and functions. Let’s say you have a
Kotlin file sampleName.kt in a package com.example as in Listing 5.13.

Listing 5.13 File-Level Declarations in Kotlin


// sampleName.kt
package com.example

class FileLevelClass // Generates class FileLevelClass


object FileLevelObject // Generates FileLevelObject as Singleton
fun fileLevelFunction() {} // Goes into generated class SampleNameKt
val fileLevelVariable = "Usable from Java" // Goes into generated class SampleNameKt

Note that classes and objects are also file-level declarations but are simply compiled to a correspond-
ing Java class as you would expect (classes generated for objects implement the singleton pattern).
The concepts of file-level properties and functions cannot be mapped to Java directly. So for the
code in Listing 5.13, the compiler generates not only the classes com.example.FileLevelClass
and com.example.FileLevelObject but also a class com.example.SampleNameKt with a static
field for the file-level property and a static method for the file-level function. The fields are private
with a public static getter (and setter).

You can explore all classes in the decompiled Java code. The static members can be called from
Java as usual after importing SampleNameKt or statically importing the members.

You can also give a shorter and more meaningful name to the generated class than the one based
on the file name. This is done in Kotlin using @file:JvmName("<YOUR_NAME>") to annotate
the entire file, as shown in Listing 5.14.
Using Kotlin Code from Java 137

Listing 5.14 Using @JvmName on File Level


// sampleName.kt
@file:JvmName("MyUtils") // Must be the first statement in the file
// ...

This way, the name of the generated class is MyUtils. You can even compile multiple Kotlin files
into a single Java class by adding @file:JvmMultifileClass and @file:JvmName with the
same name given to all of them. Be aware that using this increases the chance of name clashes
and should only be used if all incorporated file-level declarations are closely related; this is ques-
tionable because they come from separate Kotlin files, so use this judiciously.

There are several other annotations that allow you to adjust some parameters of how your Kotlin
code is mapped to Java. All of these consistently use the @Jvm prefix, and most will be discussed in
this chapter.

Calling Extensions
Extension functions and properties are typically declared on the file level and can then be called
as a method on the generated class like other top-level declarations. In contrast to Kotlin, they
cannot be called on the extension receiver type directly because there is no such feature in Java.
Listing 5.15 provides a brief example.

Listing 5.15 Calling Top-Level Extensions


// Kotlin
@file:JvmName("Notifications")

fun Context.toast(message: String) { // Top-level function => becomes static method


Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}

// Within a Java Activity (Android)


Notifications.toast(this, "Quick info...");

Here, you define an extension function that facilitates showing toast messages on Android and
use @JvmName to provide a more descriptive name for the generated class. From Kotlin, you could
simply call this extension method as toast("Quick info…") from inside an activity because
every activity is itself a context, and Context is the receiver type for this extension. With a static
import, you can achieve a similar syntax in Java but the Context must still be passed in as the
first argument.
138 Chapter 5 Interoperability with Java

Similarly, you can declare extensions inside a type declaration such as a class or object. These
can be called from Java via an instance of that encompassing type. To highlight the difference,
Listing 5.16 defines a similar extension but this time inside a Notifier class.

Listing 5.16 Calling Class-Local Extensions


// Kotlin
class Notifier {
fun Context.longToast(message: String) { // Becomes member method
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}
}

// Calling from a Kotlin Android activity


with(Notifier()) { longToast("Important notification...") }
Notifier().apply { longToast("Important notification...") }

// Calling from a Java Android Activity


Notifier notifier = new Notifier();
notifier.longToast(this, "Important notification..."); // ‘this’ is the Context arg

To call a class-local extension from outside its containing class, you need an instance of the
containing class, irrespective of whether you want to call it from Kotlin or Java. From Java, you
can call it as a static method on the instance and must again pass in the context. From Kotlin, you
have to get access to the class scope, which you can do via with or apply. Recall that, inside the
lambda expression, you can then write code as if you were inside the Notifier class. In particu-
lar, you can call longToast.

Accessing Static Members


Several of Kotlin’s language elements compile down to static fields or methods. These can be
called directly on their containing class as usual.

Static Fields
Although there’s no static keyword in Kotlin, there are several language elements in Kotlin
that will generate static fields in the Java bytecode and can thus be called as such from Java. Apart
from the examples already seen (top-level declarations and extensions), static fields may be gener-
ated from:
 Properties in object declarations
 Properties in companion objects
 Constant properties
Using Kotlin Code from Java 139

All these are shown in Listing 5.17.

Listing 5.17 Generating Static Fields on the JVM


// Kotlin
const val CONSTANT = 360

object Cache { val obj = "Expensive object here..." }

class Car {
companion object Factory { val defaultCar = Car() }
}

// Decompiled Java code (simplified)


public final class StaticFieldsKt {
public static final int CONSTANT = 360;
}

public final class Cache { // Simplified


private static final String obj = "Expensive object here..."; // private static field
public final String getObj() { return obj; } // with getter
}

public final class Car { // Simplified


private static final Car defaultCar = new Car(); // Static field
public static final class Factory {
public final Car getDefaultCar() { return Car.defaultCar; } // Nonstatic method
}
}

Properties from object declarations and companion objects produce private fields with getters
and setters. For object declarations, these are inside the Java class that corresponds to the object,
such as Cache. For companion objects, the field itself lives inside the containing class while the
getter and setter stay inside the nested class. Static members are accessed as usual from Java, for
instance as Cache.INSTANCE.getObj() or Car.Factory.getDefaultCar().

The generated static fields are private by default (except when using const) but they can again
be exposed using @JvmField. Alternately, you could use lateinit or const; as you learned,
these also expose the field. However, it’s not their main purpose but rather a side effect. Note that,
using @JvmField, the static field gets the visibility of the property itself whereas using lateinit,
it gets the visibility of the setter.
140 Chapter 5 Interoperability with Java

Static Methods
While top-level functions become static methods in the Java bytecode by default, methods
declared in named and companion objects are not static by default. You can change this using
the @JvmStatic annotation as shown in Listing 5.18.

Listing 5.18 Using @JvmStatic to Generate Static Methods


// Kotlin
object Cache {
@JvmStatic fun cache(key: String, obj: Any) { … } // Becomes a static member
}

class Car {
companion object Factory {
@JvmStatic fun produceCar() { … } // Now becomes static as well
}
}

// Inside a Java method


Cache.cache("supercar", new Car()); // Static member is callable directly on class
Cache.INSTANCE.cache("car", new Car()); // Bad practice

Car.produceCar(); // Static
Car.Factory.produceCar(); // Also possible
(new Car()).produceCar(); // Bad practice

In named objects (object declarations), using @JvmStatic allows you to call the method
directly on the class, as you’re used to for static methods. Also, it’s unfortunately possible to call
static methods on instances as well but you should avoid this because it can lead to confusing
code and adds no value. Nonstatic methods of named objects can only be called on instances,
as usual.

In companion objects, using @JvmStatic allows you to call the method directly on the
enclosing class; here, Car. It can still be called explicitly on the nested companion object
as well. Nonstatic methods can only be called as instance methods on the companion object;
here, Car.Factory. Again, the static method on an instance should be avoided.

You can also use @JvmStatic on named and companion object properties to make their
accessors static. But you cannot use @JvmStatic outside of named and companion
objects.
Using Kotlin Code from Java 141

Generating Method Overloads


Method overloading can often be avoided in Kotlin using default parameter values, saving many
lines of code. When compiling to Java, you can decide whether overloaded methods should be
generated to enable optional parameters in Java to some extent, as in Listing 5.19.

Listing 5.19 Generating Overloads with @JvmOverloads


// Kotlin
@JvmOverloads // Triggers generation of overloaded methods in Java bytecode
fun <T> Array<T>.join(delimiter: String = ", ",
prefix: String = "",
suffix: String = ""): String {
return this.joinToString(delimiter, prefix, suffix)
}

// Java
String[] languages = new String[] {"Kotlin", "Scala", "Java", "Groovy"};

// Without @JvmOverloads: you must pass in all parameters


ArrayUtils.join(languages, ";", "{", "}"); // Assumes @file:JvmName("ArrayUtils")

// With @JvmOverloads: overloaded methods


ArrayUtils.join(languages); // Skips all optional parameters
ArrayUtils.join(languages, "; "); // Skips prefix and suffix
ArrayUtils.join(languages, "; ", "Array: "); // Skips suffix
ArrayUtils.join(languages, "; ", "[", "]"); // Passes in all possible arguments

Using @JvmOverloads, the compiler generates one additional overloaded method for each
parameter with default value. This results in a series of methods where each has one fewer param-
eters, the optional one. Naturally, parameters without default value are never omitted. This
increases flexibility when calling the method from Java but still doesn’t allow as many combina-
tions as Kotlin because the order of parameters is fixed and you cannot use named parameters. For
instance, you cannot pass in a suffix without passing in a prefix.

Note that you can also use @JvmOverloads on constructors to generate overloads. Also, if all
parameters of a constructor have a default value, a parameterless constructor is generated anyway,
even without the annotation. This is done to support frameworks that rely on parameterless
constructors.
142 Chapter 5 Interoperability with Java

Using Sealed and Data Classes


Both sealed classes and data classes translate to normal classes in Java and can be used as such.
Of course, Java does not treat sealed classes specially, so they cannot be used with Java’s switch
as they can with Kotlin’s when. For instance, you must use instanceof checks to determine the
specific type of an object of the parent sealed class, as shown in Listing 5.20.

Listing 5.20 Working with Sealed Classes


// Kotlin
sealed class Component
data class Composite(val children: List<Component>) : Component()
data class Leaf(val value: Int): Component()

// Java
Component comp = new Composite(asList(new Leaf(1), new Composite(…)));

if (comp instanceof Composite) { // Cannot use ‘switch’, must use ‘if’


out.println("It's a Composite"); // No smart-casts
} else if (comp instanceof Leaf) { // No exhaustiveness inferred
out.println("It's a Leaf");
}

The sealed class becomes an abstract class, making it impossible to instantiate an object of it. Its
child classes can be used as normal classes but carry no special semantics in Java because there is
no concept for sealed classes.

Data classes can be used intuitively from Java, but there are two restrictions to keep in mind.
First, Java does not support destructuring declarations so that the componentN functions are
unnecessary. Second, there are no overloads generated for the copy method so that it has no
benefit compared to using the constructor. This is because generating all possible overloads would
introduce an exponential number of methods (with respect to the number of parameters). Also,
generating only some overloads as is the case for @JvmOverloads, there is no guarantee that this
would generate a useful subset of overloads. Hence, no overloads are generated at all for copy. All
of this is illustrated in Listing 5.21.

Listing 5.21 Working with Data Classes


// Kotlin
data class Person(val name: String = "", val alive: Boolean = true)
Using Kotlin Code from Java 143

// Java
Person p1 = new Person("Peter", true);
Person p2 = new Person("Marie Curie", false);
Person p3 = p2.copy("Marie Curie", false); // No advantage over constructor

String name = p1.getName(); // componentN() methods superfluous


out.println(p1); // Calls toString()
p2.equals(p3); // true

Visibilities
The available visibilities don’t map exactly between Kotlin and Java, plus you have top-level decla-
rations in Kotlin. So let’s explore how visibilities are mapped to Java. First, some visibilities can be
mapped trivially.
 Private members remain private.
 Protected members remain protected.
 Public language elements remain public, whether top-level or member.

Other visibilities cannot be mapped directly but are rather compiled to the closest match:
 Private top-level declarations also remain private. However, to allow calls to them from
the same Kotlin file (which may be a different class in Java), synthetic methods are
generated on the JVM. Such methods cannot be called directly but are generated to
forward calls that would not be possible otherwise.
 All of Kotlin’s internal declarations become public because package-private
would be too restrictive. Those declared inside a class go through name
mangling to avoid accidental calls from Java. For instance, an internal method
C.foo will appear as c.foo$production_sources_for_module_yourmodulename()
in the bytecode, but you’re not able to actually call them as such. You can
use @JvmName to change the name in the Java bytecode if you want to be able
to call internal members from Java.

This explains how each visibility maps to Java for both top-level declarations and members.

Getting a KClass
KClass is Kotlin’s representation of classes and provides reflection capabilities. In case you have
a Kotlin function accepting a KClass as a parameter and need to call it from Java, you can use
the predefined class kotlin.jvm.JvmClassMappingKt as in Listing 5.22. From Kotlin, you can
access both KClass and Class more easily.
144 Chapter 5 Interoperability with Java

Listing 5.22 Getting KClass and Class References


// Java
import kotlin.jvm.JvmClassMappingKt;
import kotlin.reflect.KClass;

KClass<A> clazz = JvmClassMappingKt.getKotlinClass(A.class);

// Kotlin
import kotlin.reflect.KClass

private val kclass: KClass<A> = A::class


private val jclass: Class<A> = A::class.java

Handling Signature Clashes


With Kotlin, you may declare methods that have the same JVM signature. This mostly happens
due to type erasure of generics types, meaning that type parameter information is not available at
runtime on the JVM. Thus, at runtime, Kotlin (like Java) only knows that List<A> and List<B>
have type List. Listing 5.23 demonstrates the situation.

Listing 5.23 JVM Name Clash


fun List<Customer>.validate() {} // JVM signature: validate(java.util.List)
fun List<CreditCard>.validate() {} // JVM signature: validate(java.util.List)

Here, you wouldn’t be able to call these methods from Java because there’s no way to differentiate
between the two at runtime. In other words, they end up with the same bytecode signature. As
you may expect, this is easily resolved using @JvmName, as in Listing 5.24.

Listing 5.24 Resolving JVM Name Clashes


fun List<Customer>.validate() { … }

@JvmName("validateCC") // Resolves the name clash


fun List<CreditCard>.validate() { … }

// Both can be called as validate() from Kotlin (because dispatched at compile-time)


val customers = listOf(Customer())
val ccs = listOf(CreditCard())
customers.validate()
ccs.validate()
Using Kotlin Code from Java 145

From Java, the methods are available as two static methods, FileNameKt.validate and
FileNameKt.validateCC. From Kotlin, you can access both as validate because the compiler
dispatches that to the appropriate method internally (at compile time, all necessary type informa-
tion for this is available).

Using Inline Functions


You can call inline functions from Java just like any other function, but of course they are not
actually inlined—there is no such feature in Java. Listing 5.25 demonstrates inlining when used
from Kotlin. Be aware that inline functions with reified type parameters are not callable from Java
at all because it doesn’t support inlining, and reification without inlining doesn’t work. Thus, you
cannot use reified type parameters in methods that should be usable from Java.

Listing 5.25 Calling Inline Functions (from Kotlin)


// Kotlin
inline fun require(predicate: Boolean, message: () -> String) {
if (!predicate) println(message())
}

fun main(args: Array<String>) { // Listing uses main function to show decompiled code
require(someCondition()) { "someCondition must be true" }
}

// Decompiled Java Code (of main function)


public static final void main(@NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args"); // Always generated by Kotlin
boolean predicate$iv = someCondition();
if (!predicate$iv) { // Inlined function call
String var2 = "someCondition must be true";
System.out.println(var2);
}
}

As you can see, the if statement and its body are inlined into the main method and there is no
actual call to the require method anymore. Without the inline keyword, or when calling the
method from Java, there would be a call to require instead.

Exception Handling
Because there are no checked exceptions in Kotlin, you can call all Kotlin methods from Java
without handling exceptions. This is because exceptions are then not declared in the bytecode
146 Chapter 5 Interoperability with Java

(there are no throws clauses). To allow exception handling in Java, you can use the @Throws
annotation as shown in Listing 5.26.

Listing 5.26 Generating Throws Clauses


// Kotlin
import java.io.*

@Throws(FileNotFoundException::class) // Generates throws clause in bytecode


fun readInput() = File("input.csv").readText()

// Java
import java.io.FileNotFoundException;
// …
try { // Must handle exception
CsvUtils.readInput(); // Assumes @file:JvmName("CsvUtils")
} catch (FileNotFoundException e) {
// Handle non-existing file...
}

Without the @Throws annotation, you could call the readInput method from both Kotlin and
Java without handling exceptions. With the annotation, you’re free to handle exceptions when
calling it from Kotlin, and you must handle all checked exceptions when calling it from Java.

Looking at the decompiled Java code, you can see that all the annotation does is to add a throws
FileNotFoundException to the method signature, as demonstrated in Listing 5.27.

Listing 5.27 Difference in Decompiled Java Code


// Without @Throws
public static final String readInput() { ... }

// With @Throws
public static final String readInput() throws FileNotFoundException { ... }

Using Variant Types


Regarding variant types, there’s a disparity between Kotlin and Java because Java only has use-site
variance whereas Kotlin also has declaration-site variance. Thus, Kotlin’s declaration-site variance
must be mapped to use-site variance. How is this done? Whenever an out-projected type appears
as a parameter or variable type, the wildcard type <? extends T> is automatically generated.
Conversely, for in-projected types that appear as a parameter, <? super T> is generated. This lets
you use the type’s variance in Java. Consider Listing 5.28 as an example.
Using Kotlin Code from Java 147

Listing 5.28 Mapping Declaration-Site Variance to Use Site


// Kotlin
class Stack<out E>(vararg items: E) { … }
fun consumeStack(stack: Stack<Number>) { … }

// Java: you can use the covariance of Stack


consumeStack(new Stack<Number>(4, 8, 15, 16, 23, 42));
consumeStack(new Stack<Integer>(4, 8, 15, 16, 23, 42));

The Java signature for the consumeStack method is void consumeStack(Stack<? extends
Number> stack) so that you can call it with a Stack<Number> as well as a Stack<Integer>.
You cannot see this signature in the decompiled Java code due to type erasure, but looking
at the Kotlin bytecode directly, you can find a comment stating declaration: void
consumeStack(Stack<? extends java.lang.Number>). So you’re still able to use the byte-
code tool to see what’s happening internally.

The generation of wildcard types only happens when in- or out-projected types are used as param-
eters. For return types, no such wildcards are generated because this would go against Java coding
standards. Still, you can intercept the generation process here as well. To generate wildcards where
they normally wouldn’t, you can use @JvmWildcard as in Listing 5.29. To suppress wildcards, you
can use @JvmSuppressWildcards, also shown in Listing 5.29.

Listing 5.29 Adjusting Wildcard Generation


// Kotlin
fun consumeStack(stack: Stack<@JvmSuppressWildcards Number>) { … } // No more wildcards

// Normally no wildcards are generated for return types, unless you use @JvmWildcard
fun produceStack(): Stack<@JvmWildcard Number> {
return Stack(4, 8, 15, 16, 23, 42)
}

// Java
consumeStack(new Stack<Number>(4, 8, 15, 16, 23, 42)); // Matches exactly
consumeStack(new Stack<Integer>(4, 8, 15, 16, 23, 42)); // Error: No longer allowed

Stack<Number> stack = produceStack(); // Error: No longer allowed


Stack<? extends Number> stack = produceStack(); // Inconvenient, thus bad practice

Note that you can also annotate methods or even whole classes with these to control wildcard
generation. However, most of the time you’ll be able to call Kotlin methods from Java just fine
without intercepting the generation process because declaration-site variance is mapped sensibly
by default.
148 Chapter 5 Interoperability with Java

Tip
@JvmSuppressWildcards can be the solution to errors that may be hard to debug. For
instance, a Java class that implements an interface and should override one of its methods
must match the method’s signature exactly. If the interface doesn’t include wildcards, you won’t
be able to override it without using @JvmSuppressWildcards.
A similar problem may occur if you’re using a framework that forces you to provide an imple-
mentation of a specific interface, let’s say Adapter<T>. Assume you want to implement
Adapter<Pair<String, String>>. Because Pair is covariant, you’ll actually provide an
implementation for Adapter<Pair<? extends String, ? extends String>>. So keep
in mind how variant types map to Java and, more important, remember to investigate the byte-
code and decompiled Java code.

The Nothing Type


Lastly, there’s no counterpart to Kotlin’s Nothing type in Java because even java.lang.Void
accepts null as a value. Because it’s still the closest representation of Nothing that’s available in
Java, Nothing return types and parameters are mapped to Void. As shown in Listing 5.30, this
doesn’t result in the exact same behavior.

Listing 5.30 Using the Nothing Type


// Kotlin
fun fail(message: String): Nothing { // Indicates non-terminating function
throw AssertionError(message)
}

fun takeNothing(perpetualMotion: Nothing) {} // Impossible to call from Kotlin

// Java
NothingKt.takeNothing(null); // Possible in Java (but with warning due to @NonNull)
NothingKt.fail("Cannot pass null to non-null variable"); // Cannot terminate
System.out.println("Never reached but Java doesn't know"); // Dead code

You can actually call takeNothing from Java, an action not possible from Kotlin. However,
you’ll at least receive a warning because the signature is void takeNothing(@NonNull Void
perpetualMotion). Since null is the only valid value for Void, that’s really the closest repre-
sentation of Nothing currently available in Java. Similarly, the return type of fail becomes Void
so that Java cannot infer unreachable code like Kotlin.

Lastly, using Nothing as a generic type argument generates a raw type in Java to at least provoke
unchecked call warnings. For instance, List<Nothing> becomes a raw List in Java.
Best Practices for Interop 149

Best Practices for Interop


To conclude this chapter, I want to summarize the major points by distilling best practices that
follow from the concepts and pitfalls discussed. Following these practices helps make interoper-
ability between Kotlin and Java even more seamless.

Writing Kotlin-Friendly Java Code


Calling Java code from Kotlin works seamlessly in nearly all cases. Still, there are some practices
you can follow to avoid special cases of conflict and aid interoperability even further.
 Add appropriate nullability annotations to all nonprimitive return types, parameters, and
fields to avoid ambiguous platform types.
 Use the accessor prefixes get, set, and is so that they are automatically accessible as
properties from Kotlin.
 Move lambda parameters to the end of signatures to allow for the simplified syntax where
you can pull the lambda out of the parentheses.
 Do not define methods that can be used as operators from Kotlin unless they indeed make
sense for the corresponding operator.
 Do not use identifiers in Java that are hard keywords in Kotlin12 to avoid escaping.

All of these make interoperability even more seamless and can improve the quality of your Java
code as well, such as nullability annotations.

Writing Java-Friendly Kotlin Code


As you’ve learned, calling Kotlin code from Java entails several things to keep in mind that usually
become clear when looking at the bytecode or decompiled Java code. There are also more prac-
tices you can follow to aid interoperability.
 Provide expressive names for all files with top-level declarations to avoid the default
FileNameKt naming of the class (which also leaks the language). Use @file:
JvmName("ExpressiveName") for this and consider using @file:JvmMultifileClass
if other closely related top-level declarations exist in a different Kotlin file.
 If possible, avoid Unit return types in lambda parameters of methods that are supposed
to be called from Java because this requires an explicit return Unit.INSTANCE in Java.
Future versions of Kotlin may provide ways to avoid this problem.
 Add a @Throws annotation for each checked exception in all methods if you want to be
able to handle the exception in Java. This basically lets you set your own balance between
checked and unchecked exceptions. For this, also consider possible exceptions thrown by
methods called inside that method.

12. https://kotlinlang.org/docs/reference/keyword-reference.html#hard-keywords
150 Chapter 5 Interoperability with Java

 Do not expose read-only Kotlin collections directly to Java, for instance as return types,
because they are mutable there. Instead, make defensive copies before exposing them or
expose only an unmodifiable wrapper for the Kotlin collections, such as Collections.
unmodifiableList(kotlinList).
 Use @JvmStatic on companion object methods and properties to be able to call them
directly on the containing class in Java. You can do the same for methods and properties of
named objects (object declarations) as well.
 Use @JvmField for effectively constant properties in companion objects that are not const
(because const only works for primitive types and String). Without @JvmField, they are
accessible from Java as MyClass.Companion.getEFFECTIVE_CONSTANT with an ugly
getter name. With it, they are accessible as MyClass.EFFECTIVE_CONSTANT, following
Java’s coding conventions.
 Use @JvmName on methods where the idiomatic naming differs between Kotlin and Java.
This is mostly the case for extension methods and infix methods due to the different ways
in which they are called.
 Use @JvmOverloads on any method that has default parameter values and is supposed
to be called from Java. Make sure all generated overloads make sense, and reorder the
parameters if not. Move parameters with default values to the end.

These best practices help you write idiomatic code in both Kotlin and Java even while mixing
the languages. Most of these use Kotlin’s JVM annotations to fine-tune the way in which Kotlin
compiles to Java bytecode.

Summary
In mixed-language projects and during migration, you may use Kotlin and Java simultaneously
in a project. Fortunately, most language concepts map naturally between the languages. For the
remainder, it is important to understand how Kotlin compiles to Java bytecode and how you can
write code that aids interoperability. On the Java side, this mainly includes adding nullability
information and enabling shorter syntax in Kotlin, for example, by moving lambda parameters
to the end. On the Kotlin side, it primarily encompasses using JVM annotations to adjust the
compilation process, for instance, to generate static modifiers, descriptive names, or overloaded
methods. This way, both languages can be used together without a large amount of overhead on
either side.
6
Concurrency in Kotlin

“Juggling is an illusion. . . . In reality, the balls


are being independently caught and thrown in rapid
succession. . . . It is actually task switching.”
Gary Keller

Since the emergence of multicore processors, concurrency has become an omnipresent topic.
However, concurrency is hard and comes with a list of hard-to-debug problems. This chapter
briefly introduces the basic concepts surrounding concurrency, introduces Kotlin’s coroutines
as a unique approach to concurrency, and shows how to represent a variety of asynchrony and
concurrency patterns with coroutines.

Concurrency
In today’s world of REST1 APIs and microservices, concurrency and asynchrony play a central
role for developers. On Android, apps may not actually perform many computations but rather
spend most of their time waiting—whether it be for weather data fetched from a server, user data
retrieved from a database, or an email to arrive.

For this chapter, I want to provide a working definition for several terms that are often confused
with each other: concurrency, parallelism, multitasking, multithreading, and asynchrony. The
distinction is not essential to follow the practical part on Kotlin’s coroutines and apply them in
your code. However, a solid foundation helps to put things into context and to discuss the topic.
 First, concurrency itself only means that different parts of a program can run out of order
without it affecting the end result. This does not imply parallel execution; you may have
a concurrent program running on a single-core machine with tasks that overlap in time
because processor time is shared between processes (and threads). However, concurrency
by this definition is a necessary condition for a correct parallel program—a nonconcurrent
program run in parallel produces unpredictable results.

1. Acronym stands for “representational state transfer.”


152 Chapter 6 Concurrency in Kotlin

 The terms parallelism and multitasking are used synonymously in this book and
refer to the actual parallel execution of multiple computations at the same time. In
contrast to concurrency, this requires a multicore machine (see following note). The
fact that parallel execution is also called concurrent execution may be the source of
some confusion.
 Multithreading is one particular way to achieve multitasking (parallelism), namely via
threads. A thread is intuitively a sequence of instructions managed by a scheduler. Multiple
threads can live inside the same process and share its address space and memory. Because
these threads can execute independently on multiple processors or cores, they enable
parallelism.
 Lastly, asynchrony allows operations outside of the main program flow. Intuitively, this
is usually a background task that is issued and executed without forcing the caller to
wait for its completion—such as fetching resources from a network. This is in contrast to
synchronous behavior, meaning that the caller waits for the completion of the previous
operation before performing the next operation. This may make sense if the synchronous
task is actually performing computations on the CPU but not if it is waiting for a third
party, such as a REST API, to answer.

An important aspect of asynchrony is that you don’t want to block the main thread while
waiting for a result—in fact, the terms asynchronous and nonblocking are used synonymously
in this book. On Android, blocking the main thread freezes the UI and after a while causes an
exception, resulting in a bad user experience. To achieve asynchrony, you would typically use
some implementation of futures (also called promises) such as CompletableFuture from Java 8.
In this chapter, you will discover coroutines as a more lightweight solution that also allows
writing intuitive code.

Note
For the sake of completeness, you can differentiate between parallelism on the bit level,
instruction level, data level, and functional level. On the bit level, for instance, modern proces-
sors can process a word size of 64 bits in one cycle, which is also a form of parallelism. Bit-
level parallelism is transparent for developers and does not depend on concurrency.
In this book, parallelism refers to the functional level, meaning that the same or different com-
putations are executed on the same or different data simultaneously. This is the kind of paral-
lelism that requires concurrency to guarantee a correct end result.

To connect these concepts, let us consider how they relate. Assuming that parallelism always
refers to functional-level parallelism, it relies on concurrency. Without concurrency, paral-
lelism leads to unpredictable results and randomly occurring bugs (so-called heisenbugs).
Asynchrony does not necessarily imply parallelism, at least not on your machine (if you take
into account the remote machine, you may think of it as parallelism). So while parallelism
requires a multicore machine, you may make an asynchronous call on a single-core machine
using a single thread.
Concurrency 153

As real-life examples, you can imagine parallelism as a group of people, each performing
their own task with a tennis ball: bouncing it on the floor, throwing it in the air, and so on.
Concurrency could be a single person juggling four tennis balls, focusing on one ball at a time
(time-sharing). Asynchrony could be a person doing the dishes while (nonblockingly) waiting for
his or her laundry to finish.

Here’s a brief overview of all these terms as a quick reference:


 Concurrency: Ability of units of code to execute out of order (whether parallel or time-
sharing) and still get the correct result
 Parallelism: Actual simultaneous execution of multiple units of code
 Multitasking: Same as parallelism
 Multithreading: Parallelism achieved using threads
 Asynchrony: Nonblocking operations outside of main thread

With this, let us explore common challenges introduced by concurrency and current state-of-the-
art solutions, and then dive into Kotlin’s coroutines for parallel programming.

Challenges
As you can imagine, not all programs are concurrent. More often than not, order of execution
does matter. This is especially true if the concurrent units, such as threads, share mutable state. For
instance, two threads may both read the value of the same variable, modify it, and then write it
back—in that case, the second write will override the first.

A useful example that portrays many of the common challenges is that of the dining philosophers.
Imagine five philosophers sitting around a round table, with five chopsticks distributed between
them. Each philosopher has a bowl of rice, and a chopstick to its left and to its right. Philosophers
are either in the state “eating” or in the state “thinking.” To eat, they must have both their left
and right chopstick. The philosophers are not allowed to communicate. Figure 6.1 illustrates this
situation.

In this scenario, the chopsticks are the shared resources; for each chopstick, there are two philos-
ophers competing for it. You can imagine the philosophers as parallel threads. To see how this
parallelism can cause issues, let’s say each philosopher first takes up the chopstick to his or her
left and then the one to his or her right, eats, and then put them back on the table. If all philoso-
phers start this process at the same time, they will all pick up the chopstick to the left and wait for
the other one indefinitely.

This type of problem is called a deadlock and arises whenever no parallel unit can proceed due
to circular dependencies. In the example, each philosopher is waiting for the chopstick to his
or her right. For a deadlock to happen, multiple conditions (called Coffman conditions) must
come into play, one of which is mutual exclusion. However, mutual exclusion is often necessary
to prevent other synchronization problems. It means a resource cannot be used by multiple
threads at the same time, just like a chopstick cannot be used by multiple philosophers at the
same time.
154 Chapter 6 Concurrency in Kotlin

Figure 6.1 Dining philosophers: n philosophers share n chopsticks and cannot communicate

As a next problem, you do not want two adjacent philosophers to pick up the chopstick they
compete for at the same time. This would be called race condition and arises from threads accessing
the same resource simultaneously. It is prevented using mutually exclusive access to the resource
(chopstick) so that only one thread (philosopher) can access it at a time, commonly achieved via
locks (a philosopher grabbing a chopstick locks access to it for others).

More generally, race conditions are bugs that arise because the results of parallel tasks depend
on their execution order, thus not supporting concurrency by definition. Locks are one way to
achieve concurrency, that is, to make code independent of the order of execution. For perfor-
mance reasons, you want to wait for locks (wait for access to a chopstick) in a nonblocking way so
that CPU time can be used for other computations until the lock is available. Kotlin offers locks in
its standard library via extension functions on the class java.util.concurrent.locks.Lock
and offers nonblocking lock and mutex implementations in the coroutines library.

Apart from these classical synchronization issues that come along with concurrency and paral-
lelism, there are also timing issues to consider on Android. For instance, an asynchronous
call may only finish after the activity that issued it has already been destroyed and recreated.
Consequently, a reference to the destroyed activity may be kept, causing a memory leak that
prevents the activity from being garbage-collected. Also, trying to access the destroyed activity
will crash the app. This particular problem can be solved using lifecycle-aware components intro-
duced as part of Android’s Architecture Components in 2017. That way, lifecycle-aware asynchro-
nous calls are cancelled automatically if their caller is destroyed.

Another very different issue is the fact that threads are expensive objects, and so developers learn
to create them judiciously, closely watch existing threads, and reuse previously created ones. As
Concurrency 155

you will discover in this chapter, Kotlin’s coroutines solve this problem because they are orders of
magnitude less expensive to spin up.

State-of-the-Art Solutions
In a world of multicore processors, multitasking is the primary means to improve the perfor-
mance of parallelizable algorithms. The most common way to do this is multithreading, for
example, to run a map task on a large collection in multiple threads that operate on disjoint parts
of the collection.

In general, multiple threads are allowed per process, so they share the process’ address space
and memory (see Figure 6.2 for a hierarchical breakdown). This allows for efficient communica-
tion between them but also introduces the synchronization issues discussed above because of
the shared state. Also, the fact that threads may interact with each other leads to a huge number
of possible execution paths. These are some of the reasons why concurrency is considered to
be hard. Debugging becomes more complicated and the program becomes harder to reason
about. Other models of concurrent computation, such as the actor model, reduce such problems
by removing shared mutable state. As you will learn, Kotlin’s coroutines allow both thread-like
behavior and actor-like behavior, among others.

CPU

Process

Thread Thread

Coroutine Coroutine
Coroutine Coroutine

Figure 6.2 A CPU has multiple processes, each of which can contain multiple threads that in
turn can each run a large number of coroutines

For asynchronous calls, one common approach is to use callbacks. A callback is a function c that
is passed to another function f and is executed once the result of f is available (or another event
occurs). One example of this is fetching weather data from an API and letting the callback func-
tion update the UI with the new weather report. However, you usually want to do another opera-
tion after that, and then another, and so forth. The problem with callbacks then becomes that,
with each nested operation, the indentation level in your code increases, leading to highly nested
and unreadable code (see Listing 6.1). Additional callbacks for error handling only add to the
complexity. You may know this phenomenon under the term callback hell. But not only that—
imagine doing less trivial control flow with callbacks, such as loops or try-catch blocks. The
code no longer highlights the logical workflow but how the callbacks are wired together. With
156 Chapter 6 Concurrency in Kotlin

Kotlin’s coroutines, you are able to write sequential-style code without any syntactic overhead, as
you will see later.

Listing 6.1 Asynchrony with Callbacks


// Simplified, no error handling
fetchUser(userId) { user ->
fetchLocation(user) { location ->
fetchWeather(location) { weatherData ->
updateUi(weatherData)
}
}
}

This style of programming with callbacks is also known as Continuation Passing Style (CPS).
Intuitively, a continuation is simply a callback. In CPS, continuations are always passed explicitly,
and the results of previous computations can become arguments of the next one. One conse-
quence of this is that expressions must be turned inside out because continuation order must
reflect evaluation order. For instance, you can’t call fetchWeather(fetchLocation(…)) in
Listing 6.1 because what is computed first must appear first.

Interestingly, CPS is considered hard to read and write and therefore not commonly used, but
it is standard in callback-based code. CPS is in contrast to direct style in which continuations are
implicit—the continuation of one line of code is the lines of code following it. This is what you’re
used to from sequential code. As mentioned, Kotlin preserves the possibility to write code in
sequential style even when writing asynchronous code.

Another way to use asynchrony are futures, which may appear with different names such as
CompletableFuture, Call, Deferred, or Promise, but eventually all these represent a unit
of asynchronous computation that is supposed to return a value (or an exception) later. Futures
prevent the nesting issue of callbacks and typically provide combinators to schedule additional
computation to be performed on the result, such as thenApply, thenAccept, or thenCombine,
as shown in Listing 6.2.

Listing 6.2 Asynchrony with Futures


// Simplified, no error handling
fetchUser(userId)
.thenCompose { user -> fetchLocation(user) }
.thenCompose { location -> fetchWeather(location) }
.thenAccept { weatherData -> updateUi(weatherData) }

These combinators allow a fluent programming style and pass along exceptions, but you have to
memorize lots of combinators; also, they’re not consistent between implementations, adding to
Kotlin Coroutines 157

the cognitive overhead. So even with the improvements of futures compared to callbacks, Kotlin
coroutines still improve readability because code looks natural, without the need for such combi-
nators, as shown later.

Another way to write asynchronous code is to use async-await language constructs as in C#,
TypeScript, or Python. Here, async and await are language keywords. An async function is one
that performs an asynchronous operation, and await is used to explicitly wait for the comple-
tion of such an operation, as in Listing 6.3. You could say that await transitions back into the
synchronous world, where you wait for an operation to finish before proceeding to the next.
Kotlin’s concept of suspending functions generalizes the async-await pattern without introducing
keywords for it. Later in this chapter, the rationale for not simply adopting the well-known async-
await pattern will become clearer.

Listing 6.3 Asynchrony with async-await Pattern (C#)


// C#: Notice the return type Task<Location> instead of Location
async Task<Location> FetchLocation(User user) { … }
var location = await FetchLocation(user) // Need await keyword to fetch location

Regarding synchronization issues, state-of-the-art solutions include concurrent data structures,


locks, and avoiding shared mutable state. First, concurrent data structures allow concurrent
access without disrupting the integrity of the stored data. They facilitate parallel programming
because they can be used for shared mutable state. Second, locks restrict access to a critical section
to one concurrent unit to avoid race conditions. More generally, semaphores restrict access to a
given number of concurrent units. Third, avoiding shared mutable state is a good practice and
brings forth approaches such as the previously mentioned actor model in which concurrent units
communicate exclusively via message passing and thus have no shared state at all. These concepts
all apply to concurrency with Kotlin as well and go more into the direction of coding practices
than language design.

Kotlin Coroutines
Having discussed the virtues, challenges, and approaches of concurrency, this section intro-
duces Kotlin’s prime feature for concurrency and asynchrony: coroutines. Conceptually, you can
imagine coroutines as very lightweight threads. However, they are not threads; in fact, millions of
coroutines may run in a single thread. Also, a coroutine is not bound to one specific thread. For
example, a coroutine may start on the main thread, suspend, and resume on a background thread
later. You can even define how coroutines are dispatched, which is useful to distribute operations
between the UI thread and background threads on Android and other UI frameworks.

Coroutines are similar to threads in that they have a similar lifecycle: They get created, get
started, and can get suspended and resumed. Like threads, you can use coroutines to implement
parallelism, you can join coroutines to wait for their completion, and you can cancel coroutines
explicitly. However, compared to threads, coroutines take up much fewer resources so there is no
need to keep close track of them as you do with threads; you can usually create a new coroutine
158 Chapter 6 Concurrency in Kotlin

for every asynchronous call without worrying about overhead. Another difference is that threads
use preemptive multitasking, meaning that processor time is shared between threads by a sched-
uler. You can imagine threads as being selfish; they do not think about other threads and use all
the processor time they can get for themselves. Coroutines, on the other hand, use cooperative
multitasking in which each coroutine itself yields control to other coroutines. There is no separate
mediator involved to support fairness.

Note
Coroutines were experimental until Kotlin 1.3. At the time of writing, Kotlin 1.3 was not
released in its stable version yet. Even though some details of coroutines as presented here
may have changed, the concepts discussed remain and all code examples should still work
as described here or with small changes.
Any such changes and coroutine updates are covered on the companion website at
kotlinandroidbook.com, along with adapted code listings where necessary.

Setup
Before diving deeper into coroutines and exploring code examples, you should set up coroutines
in a Kotlin project to follow along: Add the core dependency to the dependencies section (not
the one inside of buildscript) of your app’s build.gradle file:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.25.3"

For Android, also add the Android-specific dependency:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.25.3"

You can replace the version number with the latest one.2 With this, you should now be able to use
coroutines in your project. If you face issues with your setup, you can find a more detailed setup
guide on GitHub.3

At the time of writing, coroutines don’t play well with .kts scripts, so the code listings in this
chapter use main functions.

Concepts
With coroutines, the Kotlin team intended to generalize the concepts of async-await and genera-
tors (asynchronous iterators). They also wanted coroutines to be independent from any specific
implementation of futures, and they wanted to be able to wrap asynchronous APIs such as Java
NIO (nonblocking I/O). All of this is possible with coroutines. On top of that, coroutines allow
you to write natural function signatures without callbacks or Future<T> return values, let you
write sequential-style code without callbacks or combinators, and make concurrency explicit

2. Find the latest version here: https://github.com/Kotlin/kotlinx.coroutines/releases


3. https://github.com/Kotlin/kotlinx.coroutines/blob/master/README.md#using-in-your-projects
Kotlin Coroutines 159

at the call site. In fact, it is encouraged to always make concurrency and asynchrony explicit in
idiomatic Kotlin code. You will see how this is done in this chapter.

With coroutines, the example portrayed in Listings 6.1 and 6.2 can be written as in Listing 6.4. As
you can see, coroutines allow you to write asynchronous code in the sequential style that you are
used to and that represents the logical workflow of the program. Also, you could add conditions,
loops, try-catch blocks, and other control flow constructs naturally to the code in Listing 6.4—
which is not trivial with callbacks.

Listing 6.4 Asynchrony with Coroutines


val user = fetchUser(userId) // Asynchronous call in sequential style
val location = fetchLocation(user)
val weatherData = fetchWeather(location)
updateUi(weatherData)

One thing that may be confusing without IDE support is that you would expect code that looks
like this to be blocking. For instance, in the first line, you would expect the current thread
to be blocked until the user info has been received, preventing the thread from performing
other tasks in the meantime. However, these functions are suspending functions, meaning they
can suspend execution in a nonblocking way, and Android Studio highlights those in the left
margin of the code editor so that you’re aware of suspension points and the fact that asyn-
chrony is used.

Suspending Functions
Following the intuition provided by Roman Elizarov from the Kotlin team, suspending functions
are functions with an additional superpower: they may suspend their execution in a nonblocking
way at well-defined points. This means that the coroutine that is currently running the suspend-
ing function is detached from the thread on which it was operating, and then waits until it is
resumed (potentially on another thread). In the meantime, the thread is free to perform other
tasks. Listing 6.5 defines fetchUser as a suspending function.

Listing 6.5 Declaring a Suspending Function


suspend fun fetchUser(): User { … }

The suspend modifier transforms a normal function into a suspending function. In principle,
nothing more is required. As you can see, the function signature itself is completely natural,
without additional parameters or a future wrapping the return value. Compare this to the func-
tion signature for callback-based or future-based asynchrony in Listing 6.6. Callbacks introduce
an additional parameter, whereas futures wrap the desired return value.
160 Chapter 6 Concurrency in Kotlin

Listing 6.6 Function Signatures for Callbacks and Futures


// Callback-based
fun fetchUser(callback: (User) -> Unit) { … }

// Future-based
fun fetchUser(): Future<User> { … }

With coroutines, both the actual function signature and the calling code look natural. At this
point, it’s important to understand that the suspend modifier is redundant unless the function
actually contains a suspension point. Suspension points are the mentioned “well-defined points”
at which a suspending function may suspend. Without a suspension point, the function is still
effectively a blocking function because there is no way for it to suspend execution.

So how can you introduce such a suspension point? By calling another suspending function.
All suspending functions from the Kotlin standard library work for this because, in the end, all
of these end up calling the low-level function suspendCoroutine. You can also call suspend-
Coroutine directly, but it is typically used to implement higher-level APIs for libraries. Assuming
all fetch functions are declared as suspending, Listing 6.7 denotes the suspension points similar to
how Android Studio does.

Listing 6.7 Asynchrony with Coroutines


suspend fun updateWeather() {
val user = fetchUser(userId) // Suspension point #1
val location = fetchLocation(user) // Suspension point #2
val weatherData = fetchWeather(location) // Suspension point #3
updateUi(weatherData)
}

The updateWeather function may yield control in a nonblocking way at each of the suspen-
sion points—and only at these points. This allows waiting until the result of each call is avail-
able without blocking the CPU. The three calls are executed sequentially by default so that, for
instance, line three is only executed once the second line completes.

This contrasts to the default behavior of async functions in languages like C#, which work
asynchronously by default—to make them behave sequentially, developers have to call await
on them. The Kotlin team decided to make sequential behavior the default for two reasons.
First, they expect it to be the most common use case and align with what developers are used to.
Second, they encourage developers to always make asynchrony explicit. For example, a better
name for a future-based fetchUser function would be fetchUserAsync or asyncFetchUser,
so that asynchrony is explicit at the call site (see Listing 6.8). Because suspending functions are
sequential by default, no such prefix or suffix is necessary there.
Kotlin Coroutines 161

Listing 6.8 Naming Asynchronous Functions


fun fetchUserAsync(): Future<User> { … }

// Call-site
val user = fetchUserAsync() // Asynchrony is explicit, so you expect a Future<User>

Excited about suspending functions, you may try to call a suspending function from within main
or from onCreate in your Android app but find that this is not possible. If you think about it, this
makes sense: Your suspending function has the superpower to suspend execution without block-
ing the thread, but main and onCreate are just normal functions and don’t possess that super-
power. So naturally, they cannot call a suspending function. Suspending functions can only be
called from another suspending function, from a suspending lambda, from a coroutine, or from
a lambda that is inlined into a coroutine (because then it is effectively called from the coroutine).
The Kotlin compiler will give you a friendly reminder of this when you try to call a suspending
function differently, as shown in Listing 6.9. Intuitively, you have to enter the world of suspending
functions first. This is done using coroutine builders, which are introduced in the next section.

Listing 6.9 Cannot Call Suspending Function from Nonsuspending Function


fun main(args: Array<String>) {
// Error: Suspend function must be called from coroutine or other suspend function
updateWeather()
}

Before exploring different ways to enter the world of suspending functions, I want to give more
examples of how suspending functions compose naturally and allow control flow structures.
Basically, they abstract away much of the complexity of concurrency and allow you to write
code as you’re used to. For instance, imagine each user has a list of locations for which they want
weather info. You can simply call suspending functions from a loop as in Listing 6.10.

Listing 6.10 Suspending Functions and Loops


suspend fun updateWeather(userId: Int) {
val user = fetchUser(userId)
val locations = fetchLocations(user) // Now returns a list of locations
for (location in locations) { // Uses loop naturally in suspending function
val weatherData = fetchWeather(location)
updateUi(weatherData, location)
}
}
162 Chapter 6 Concurrency in Kotlin

Similarly, you can use try-catch expressions as usual to handle exceptions, as demonstrated in
Listing 6.11. This provides a familiar way to handle exceptions on different levels of granularity
without the need for additional failure callbacks or failure combinators.

Listing 6.11 Suspending Functions and Exception Handling


suspend fun updateWeather(userId: Int) {
try { // Uses try-catch naturally within suspending function
val user = fetchUser(userId)
val location = fetchLocation(user)
val weatherData = fetchWeather(location)
updateUi(weatherData)
} catch(e: Exception) {
// Handle exception
} finally {
// Cleanup
}
}

Due to the fact that suspending functions retain the natural return value, they can also be
combined with Kotlin’s powerful standard library and higher-order functions. For instance,
Listing 6.10 could be rewritten as in Listing 6.12 because fetchLocations directly returns a
List<Location> (the natural return type).

Listing 6.12 Suspending Functions and Higher-Order Functions


suspend fun updateWeather(userId: Int) {
val user = fetchUser(userId)
fetchLocations(user).forEach { // Can use all higher-order fcts. as usual
val weatherData = fetchWeather(it) // Can be called inside higher-order function
updateUi(weatherData, it)
}
}

In short, you can usually use suspending functions just like you would use any other function,
except that you can only call them from a coroutine or another suspending function. In fact,
a coroutine requires at least one suspending function in order to start, so every coroutine is
supplied with a suspending function as its task upon creation. This is typically in the form of a
suspending lambda that is passed into a coroutine builder to start a new coroutine, as discussed in
the following section.
Kotlin Coroutines 163

Coroutine Builders
Coroutine builders open the gate to the world of suspending functions. They are the starting
point to spin up a new coroutine and to make an asynchronous request. As mentioned, coroutine
builders accept a suspending lambda that provides the coroutine’s task. There are three essential
coroutine builders with different intentions; they can be characterized very briefly as follows.
 launch is used for fire-and-forget operations without return value.
 async is used for operations that return a result (or an exception).
 runBlocking is used to bridge the regular world to the world of suspending functions.

Bridge to Suspending World with runBlocking


The characterization of runBlocking may sound confusing at first but it is exactly what solves
the problem from Listing 6.9 where bridging between the two worlds is required. Listing 6.13
demonstrates how to use runBlocking to be able to call suspending functions from main.

Note
All listings in this book assume that Kotlin 1.3 has been released by the time you’re reading
this so that coroutines are no longer experimental. If they are still experimental, simply replace
the package kotlinx.coroutines by kotlinx.coroutines.experimental in all imports
in this chapter, as well as in Chapter 7, Android App Development with Kotlin: Kudoo App, and
Chapter 8, Android App Development with Kotlin: Nutrilicious.

Listing 6.13 Using runBlocking


import kotlinx.coroutines.runBlocking

fun main(args: Array<String>) {


runBlocking {
// Can call suspending functions inside runBlocking
updateWeather()
}
}

As a coroutine builder, runBlocking creates and starts a new coroutine. Living up to its name,
it blocks the current thread until the block of code passed to it completes. The thread can still be
interrupted by other threads, but it cannot do any other work. This is why runBlocking should
never be called from a coroutine; coroutines should always be nonblocking. In fact, runBlocking
is meant to be used in main functions and in test cases for suspending functions.
164 Chapter 6 Concurrency in Kotlin

Note that runBlocking, like all coroutine builders, is not a keyword but just a function, similar
to the scoping functions with, apply, and run. The lambda passed into the coroutine builder is a
suspending lambda because of the signature of runBlocking (see Listing 6.14).

Listing 6.14 Signature of runBlocking (Simplified)


fun <T> runBlocking(block: suspend () -> T) // Blocks thread until coroutine finishes

Kotlin allows you to define a lambda parameter as a suspending function by prepending the
function type with the suspend modifier. This is called a suspending function type (also suspend-
ing lambda), similar to regular function types such as (String) -> Int. The modifier on the
parameter makes the passed lambda suspending so that other suspending functions can be called
inside runBlocking. This is the trick that allows you to enter the world of suspending functions.
A more idiomatic way to call runBlocking is shown in Listing 6.15.

Listing 6.15 Idiomatic Way to Use runBlocking


fun main(args: Array<String>) = runBlocking<Unit> { // Allows suspend calls in main
updateWeather()
}

// In a test case…
@Test fun testUpdateWeather() = runBlocking { … updateWeather() … }

The idiomatic way uses shorthand function notation to call runBlocking. In the case
of the main function, you may have to explicitly specify Unit as the return type by using
runBlocking<Unit>. This is required if the return value of the lambda is not Unit (as defined by
its last line) because the main function must return Unit. To avoid confusion, you may want to
consistently use runBlocking<Unit> because that makes sure the return type is always correct,
independent of the lambda’s return type. This is only necessary when using it with shorthand
notation for a main function. The following listings will assume Kotlin scripts again, so there will
be no main functions unless necessary.

Note
runBlocking should never be used from within a coroutine, only in regular code to use
suspending functions.

Fire-and-Forget Coroutines using launch


Intuitively, the launch coroutine builder is used for fire-and-forget coroutines that execute inde-
pendent of the main program. These are similar to threads in that they are typically used to intro-
duce parallelism and uncaught exceptions from the coroutine are printed to stderr and crash
the application. Listing 6.16 gives an introductory example.
Kotlin Coroutines 165

Listing 6.16 Launching a New Coroutine


import kotlinx.coroutines.*

// Launches non-blocking coroutine


launch {
println("Coroutine started") // 2nd print
delay(1000) // Calls suspending function from within coroutine
println("Coroutine finished") // 3rd print
}

println("Script continues") // 1st print


Thread.sleep(1500) // Keep program alive
println("Script finished") // 4th print

This code launches one new coroutine that first delays for a second—in a nonblocking way—and
then prints a message. The delay function is a suspending function from the standard library
and thus represents a suspension point. However, even the print statement before the delay is
only printed after the print statement that follows launch. This simple example already uncovers
Kotlin’s approach to coroutine scheduling: Coroutines are appended to the target thread for later
execution. This is the same behavior as in JavaScript. In contrast, C# executes each async function
immediately, until its first suspension point. Following that approach, "coroutine started"
would be printed before "Script continues" in Listing 6.16. This is more efficient, but the
JavaScript approach is more consistent and less prone to errors, which is why the Kotlin team chose
that option.

You can still use the C# behavior using launch(CoroutineStart.UNDISPATCHED). For the
sake of completeness, I want to mention that even with the default CoroutineStart, a corou-
tine may start immediately—namely if the dispatcher doesn’t need to dispatch. Dispatchers are
discussed in detail in the following section. Still, the rule of thumb is that Kotlin schedules corou-
tines for later execution on the target thread.

Note
The default “target thread” for coroutine builders is determined by the DefaultDispatcher
object. At the time of writing, this default dispatcher is CommonPool, which is a thread pool
using ForkJoinPool.commonPool if that is available (since Java 8), otherwise it tries to cre-
ate a new instance of ForkJoinPool (since Java 7) and otherwise it falls back to creating a
thread pool with Executors.newFixedThreadPool. However, CommonPool is planned to be
deprecated in the future so might no longer be the default dispatcher when you’re reading this.
In any case, the default dispatcher will still be a thread pool that is intended for CPU-bound
operations. Thus, its pool size is equal to the number of cores. It should not be used for network
calls or I/O operations. It is a good practice to have separate thread pools for such network calls
and for I/O operations, and coroutines that access the UI must always use the main thread.
166 Chapter 6 Concurrency in Kotlin

The code in Listing 6.16 can be improved. First, the code currently mixes suspending and regular
functions because it uses the nonblocking delay as well as the blocking Thread.sleep. By using
runBlocking, this can be made consistent because Thread.sleep can be replaced with delay.
However, yielding control for a fixed time (using delay) to wait for a parallel job is not good
practice. Instead, you can use the Job object that launch returns to await completion of the job
explicitly.

Listing 6.17 applies both these improvements. One should note that waiting is only required
because active coroutines do not prevent the program from exiting. Also, because join is a
suspending function, runBlocking is still required.

Listing 6.17 Waiting for a Coroutine


import kotlinx.coroutines.*

runBlocking { // runBlocking is required to call suspending ‘join’


val job = launch { // launch returns a Job object
println("Coroutine started")
delay(1000)
println("Coroutine finished")
}

println("Script continues")
job.join() // Waits for completion of coroutine (non-blocking)
println("Script finished")
}

So far, this exact behavior can be achieved with threads just as well. Kotlin even provides corou-
tine builder-like syntax for thread creation with thread { … }. But with coroutines, you can
easily start 100,000 jobs to implement the scenario of 100,000 workers working in parallel. This is
shown in Listing 6.18. Doing this with threads would likely cause an out-of-memory exception,
because each thread takes around 1MB of memory, or it would at least take orders of magnitude
longer to finish due to large overhead for thread creation and context switching.

Listing 6.18 Launching 100,000 Coroutines


import kotlinx.coroutines.*

runBlocking {
// Launch 100,000 coroutines
val jobs = List(100_000) {
launch {
delay(1000)
Kotlin Coroutines 167

print("+")
}
}
jobs.forEach { it.join() }
}

This prints “+” 100,000 times without an out-of-memory error. The listing uses the List
constructor that accepts the list size and a lambda that defines how each list item is created.
Because launch returns a Job, the result is a List<Job> that can be used to wait for all corou-
tines to complete. As shown in Listing 6.19, it can also be used to cancel a coroutine. Note that
repeat is just a utility function to write simple for loops more concisely.

Listing 6.19 Cancelling a Coroutine


import kotlinx.coroutines.*

runBlocking {
val job = launch {
repeat(10) {
delay(300) // Cooperative delay from stdlib
println("${it + 1} of 10...") // Only 1 of 10, 2 of 10, 3 of 10 are printed
}
}

delay(1000)
println("main(): No more time")
job.cancel() // Can control cancellation on per-coroutine level
job.join() // Then wait for it to cancel
println("main(): Now ready to quit")
}

Once job.cancel() is called, the coroutine receives a signal to cancel its execution. This signal
is represented by a CancellationException. This exception does not print to stderr because
it is an expected way for a coroutine to stop. Similar to cooperative multitasking, coroutines also
follow cooperative cancellation. This means that coroutines must explicitly support cancellation.
You can do this by regularly calling any suspending function from Kotlin’s standard library as
they are all cancellable, thus making your coroutine cancellable. Another way is to regularly
check the isActive property available inside the coroutine scope. First, consider the noncoop-
erative implementation in Listing 6.20, which is not cancellable.
168 Chapter 6 Concurrency in Kotlin

Listing 6.20 Noncooperative Coroutine Cannot Be Cancelled


import kotlinx.coroutines.*

runBlocking {
val job = launch {
repeat(10) {
Thread.sleep(300) // Non-cooperative
println("${it + 1} of 10...") // All ten iterations are executed
}
}

delay(1000)
job.cancelAndJoin() // Cancel is ignored, will wait for another 2 seconds
}

This code uses the noncooperative Thread.sleep instead of Kotlin’s cooperative delay. This
is why, even though cancellation is issued after 1 second, the coroutine will continue running
until the end, keeping the thread busy in the meantime. Therefore, the call to the utility func-
tion cancelAndJoin has to wait for about 2 seconds. The easiest fix in this case is to use delay
instead of Thread.sleep. That way, the coroutine can be cancelled whenever it reaches delay,
thus introducing ten cancellation points.

Another way that doesn’t require calling a suspending function from the standard library is to
check isActive manually, as done in Listing 6.21.

Listing 6.21 Making Coroutine Cooperative Using isActive


import kotlinx.coroutines.*

runBlocking {
val job = launch {
repeat(10) {
if (isActive) { // Cooperative
Thread.sleep(300)
println("${it + 1} of 10...")
}
}
}

delay(1000)
job.cancelAndJoin()
}
Kotlin Coroutines 169

This introduces ten points at which the coroutine can be cancelled—each time isActive is
checked. Cancellation causes isActive to be false, thus making the coroutine jump through
the remaining iterations quickly, evaluating the condition to false each time and finishing execu-
tion. So to achieve cooperative cancellation, you just have to make sure that you check isActive
often enough and that isActive being false causes the coroutine to finish.

Asynchronous Calls with Return Value Using async


The next essential coroutine builder is async, which is used for asynchronous calls that return
a result or an exception. This is highly useful for REST API requests, fetching entries from a
database, reading contents from a file, or any other operation that introduces wait times and
fetches some data. If the lambda passed to async returns type T, then the async call returns a
Deferred<T>. Deferred is a lightweight future implementation from the Kotlin coroutines
library. To get the actual result, you must call await on it.

As in C#, await waits for the completion of the async task—of course nonblocking—to be
able to return the result. However, unlike C#, Kotlin has no await keyword, it’s just a function.
Listing 6.22 demonstrates how easily async allows you to make multiple asynchronous calls
simultaneously (if they’re independent).

Listing 6.22 Starting Asynchronous Tasks


import kotlinx.coroutines.*

fun fetchFirstAsync() = async { // Return type is Deferred<Int>


delay(1000)
294 // Return value of lambda, type Int
}

fun fetchSecondAsync() = async { // Return type is Deferred<Int>


delay(1000)
7 // Return value of lambda, type Int
}

runBlocking {
// Asynchronous composition: total runtime ~1s
val first = fetchFirstAsync() // Inferred type: Deferred<Int>
val second = fetchSecondAsync() // Inferred type: Deferred<Int>
val result = first.await() / second.await() // Awaits completion; now type is Int
println("Result: $result") // 42
}
170 Chapter 6 Concurrency in Kotlin

The two asynchronous functions both start a new coroutine and simulate network calls that each
take 1 second and return a value. Because the lambdas passed to async return Int, the two func-
tions both return a Deferred<Int>. Following naming conventions, the function names carry
an “async” suffix so that asynchrony is explicit at the call site and a Deferred can be expected.
Calling first.await() suspends execution until the result is available and returns Int, the
unwrapped value. So when the code reaches the expression first.await(), it will wait for about
a second before moving on to second.await(), which requires no further waiting at that point
because that asynchronous job is also finished after 1 second. Therefore, the total execution time
of Listing 6.22 is about one second.

With suspending functions in place of async, it would take 2 seconds because suspending func-
tions are sequential by default. Similarly, calling await on the first Deferred before even issuing
the second asynchronous call, as in Listing 6.23, also results in a runtime of around 2 seconds.
This is because the first await call suspends execution for a second, and only after that, the
second coroutine is created and can start working.

Listing 6.23 Running Asynchronous Functions Synchronously


import kotlinx.coroutines.*

runBlocking {
// Effectively synchronous: total runtime ~2s
val first = fetchFirstAsync().await() // Inferred type: Int (runtime ~1s)
val second = fetchSecondAsync().await() // Inferred type: Int (runtime ~1s)
}

The two asynchronous functions used so far are not idiomatic Kotlin code. Kotlin encourages
sequential behavior as the default, and asynchrony as an opt-in option. However, in Listing 6.23,
asynchrony is the default and synchrony must be forced by calling await directly after each call.
Listing 6.24 shows the idiomatic approach.

Listing 6.24 Idiomatic Asynchrony


import kotlinx.coroutines.*

suspend fun fetchFirst(): Int {


delay(1000); return 294
}

suspend fun fetchSecond(): Int {


delay(1000); return 7
}

runBlocking {
Kotlin Coroutines 171

val a = async { fetchFirst() } // Asynchrony is explicit


val b = async { fetchSecond() } // Asynchrony is explicit
println("Result: ${a.await() / b.await()}")
}

Notice that the two fetch functions are now suspending functions, have the natural return value,
and there is no need for an “async” suffix. This way, they behave sequentially by default and you
can opt in to asynchrony simply by wrapping the call into async { … }. This keeps the asyn-
chrony explicit.

In contrast to launch, you should not forget about coroutines started with async because they
carry a result or an exception. Unless processed, an exception is swallowed silently. Listing 6.25
shows how to handle the failure case. Note that await fulfills two functions: It returns the result
if the operation completed successfully, and it rethrows the exception if the operation completed
exceptionally. Thus, it can be wrapped into a try-catch block.

Listing 6.25 Handling Exceptions from Asynchronous Calls


import kotlinx.coroutines.*

runBlocking {
val deferred: Deferred<Int> = async { throw Exception("Failed...") }

try {
deferred.await() // Tries to get result of asynchronous call, throws exception
} catch (e: Exception) {
// Handle failure case here
}
}

You can also check the state of a Deferred directly using the properties isCompleted and
isCompletedExceptionally. Be aware that the latter being false does not mean the opera-
tion completed successfully, it may instead still be active. In addition to these, there is also a
deferred.invokeOnCompletion callback in which you may use deferred.getComple-
tionExceptionOrNull to get access to the thrown exception. In most cases, the approach in
Listing 6.25 should suffice and is more readable.

Note
Notice the similar relations between launch/join and async/await. Both launch and async
are used to start a new coroutine that performs work in parallel. With launch, you may wait for
completion using join, whereas with async, you normally use await to retrieve the result.
While launch returns a Job, async returns a Deferred. However, Deferred implements
the Job interface so you may use cancel and join on a Deferred as well, although this is
less common.
172 Chapter 6 Concurrency in Kotlin

Coroutine Contexts
All coroutine builders accept a CoroutineContext. This is an indexed set of elements such as the
coroutine name, its dispatcher, and its job details. In this regard, CoroutineContext is similar
to a set of ThreadLocals, except that it’s immutable. This is possible because coroutines are
so lightweight that you can simply create a new one if the context should be modified. The two
most important elements of a coroutine context are the CoroutineDispatcher that decides on
which thread the coroutines run, and the Job that provides details about the execution and can
be used to spawn child coroutines. Apart from these two, context also contains a CoroutineName
and a CoroutineExceptionHandler.

Coroutine Dispatchers
Whenever a coroutine resumes, its dispatcher decides on which thread it resumes. Listing 6.26
shows how to pass a CoroutineDispatcher to launch a coroutine on the UI thread on Android.

Listing 6.26 Launching a Coroutine on the UI Thread (Android)


import kotlinx.coroutines.android.UI
import kotlinx.coroutines.launch

launch(UI) { // The UI context is provided by the coroutines-android dependency


updateUi(weatherData)
}

The UI dispatcher is provided by the Android-specific coroutines package, which is imported with
the Gradle dependency org.jetbrains.kotlinx:kotlinx-coroutines-android. Such UI
dispatchers exist for all major UI frameworks, including Android, Swing, and JavaFX. You can also
implement custom ones. The Android UI dispatcher is defined as a top-level public property as in
Listing 6.27. It refers to Android’s main looper, which lives in the main thread.

Listing 6.27 Definition of UI Context on Android


val UI = HandlerContext(Handler(Looper.getMainLooper()), "UI")

Before resuming a coroutine, the CoroutineDispatcher has a chance to change where it


resumes. A coroutine with the default dispatcher always resumes on one of the CommonPool
threads and therefore jumps between threads with every suspension. All the UI dispatchers
make sure that execution always resumes on the UI thread. In contrast, a coroutine with default
context uses the CommonPool and thus may resume on any of the background threads from
this pool. Listing 6.28 shows three equivalent launch statements (at the time of writing; the
DefaultDispatcher is subject to change and may no longer be CommonPool).4

4. Please visit the companion website, https://kotlinandroidbook.com/, for any such updates.
Kotlin Coroutines 173

Listing 6.28 Default Coroutine Dispatcher


launch { … }
launch(DefaultDispatcher) { … }
launch(CommonPool) { … }

Another possibility is to run a coroutine in a new, dedicated thread. Keep in mind that this intro-
duces the cost of thread creation and therefore removes the advantage of lightness. Listing 6.29
shows how to do this using newSingleThreadContext. This creates a pool of one thread. You
can also create a thread pool with multiple threads using newFixedThreadPoolContext.

Listing 6.29 Dispatching Onto New Thread


launch(newSingleThreadContext("MyNewThread")) { … } // Runs coroutine in new thread

If you want a coroutine to be completely unconfined from any threads (and the CommonPool),
you can use the Unconfined dispatcher as in Listing 6.30. This starts the coroutine in the current
call frame and then resumes it wherever the resuming suspension function runs. Basically, the
unconfined dispatcher never intercepts resumption and just lets the suspending function that
resumes the coroutine decide which thread to run on. An example that illustrates this behavior is
given later in Listing 6.32.

Listing 6.30 Unconfined Dispatcher


launch(Unconfined) { … }

The last dispatcher, or more precisely, CoroutineContext that contains a dispatcher, is


coroutineContext (see Listing 6.31). This is a top-level property that’s available inside every
coroutine and represents its context. By using a coroutine’s context to launch another one, that
other one becomes a child coroutine. To be precise, the parent-child relationship is not induced
by the dispatcher but by the job that is also part of the context. Cancelling a parent job recur-
sively cancels all its child jobs (still relying on cooperative cancellation).

Listing 6.31 Creating a Child Coroutine


launch(coroutineContext) { … }

To get a better feeling for the differences among these dispatchers and what behavior results from
them, Listing 6.32 uses all of them and compares which threads they are dispatched to before and
after a suspension.
174 Chapter 6 Concurrency in Kotlin

Listing 6.32 Comparing Dispatchers


import kotlinx.coroutines.*
import kotlin.coroutines.experimental.coroutineContext

runBlocking {
val jobs = mutableListOf<Job>()

// DefaultDispatcher (CommonPool at the time of writing)


jobs += launch {
println("Default: In thread ${Thread.currentThread().name} before delay")
delay(500)
println("Default: In thread ${Thread.currentThread().name} after delay")
}

jobs += launch(newSingleThreadContext("New Thread")) {


println("New Thread: In thread ${Thread.currentThread().name} before delay")
delay(500)
println("New Thread: In thread ${Thread.currentThread().name} after delay")
}

jobs += launch(Unconfined) {
println("Unconfined: In thread ${Thread.currentThread().name} before delay")
delay(500)
println("Unconfined: In thread ${Thread.currentThread().name} after delay")
}

jobs += launch(coroutineContext) {
println("cC: In thread ${Thread.currentThread().name} before delay")
delay(500)
println("cC: In thread ${Thread.currentThread().name} after delay")
}

jobs.forEach { it.join() }
}

This code starts four coroutines inside runBlocking that cover all the discussed dispatchers.
Each coroutine first executes a print statement, then reaches a delay that suspends it, and next
resumes for another print statement. The interesting part here is to see in which thread each
coroutine runs before and after the suspension. Table 6.1 gives an overview of the results.
Kotlin Coroutines 175

Table 6.1 Results of Dispatching from Listing 6.32


Coroutine Dispatcher Before Delay After Delay
Default Dispatcher In thread ForkJoinPool In thread ForkJoinPool
(CommonPool) .commonPool-worker-1 .commonPool-worker-1
Single-thread context In thread New Thread In thread New Thread
Unconfined In thread main In thread kotlinx.coroutines.
DefaultExecutor
coroutineContext In thread main In thread main

With the default dispatcher, the coroutine is always dispatched to some worker thread from the
pool. Running this with Java 8 as the compilation target uses the ForkJoinPool.commonPool as
expected. With a single-thread context, the coroutine always executes within that particular thread.
For Unconfined and coroutinesContext, it is important to notice that the current thread is the
main thread because that is where runBlocking executes. Because Unconfined never actively
dispatches anywhere, the coroutines just start off in the current main thread. The same goes for
coroutineContext because it inherits the context from runBlocking. However, after the suspen-
sion, the unconfined coroutine resumes wherever the suspending function tells it to. Since delay
uses a so-called DefaultExecutor from the kotlinx.coroutine package, that is where it resumes.
In contrast, coroutineContext always resumes in the main thread that it inherited from its parent.

In many cases, you want more fine-grained control of the context. More specifically, you want
different parts of a coroutine to run on different threads. A typical scenario is making asyn-
chronous calls to fetch data on a background thread and then displaying it in the UI once it is
available. The updateWeather function presented above resembles this pattern. A better imple-
mentation for it is given in Listing 6.33.

Listing 6.33 Switching Context Inside Coroutine


import kotlinx.coroutines.*
import kotlinx.coroutines.android.UI

suspend fun updateWeather(userId: Int) {


val user = fetchUser(userId)
val location = fetchLocation(user)
val weatherData = fetchWeather(location)

withContext(UI) {
updateUi(weatherData)
}
}

// Call-site
launch { updateWeather() }
176 Chapter 6 Concurrency in Kotlin

Running part of a coroutine in a specific context is done using withContext. That way, only the
code that touches the UI is executed on the UI thread. The suspending function itself can now
be launched in a thread pool to perform the asynchronous calls without affecting the UI thread
at all until the final result is available. Note that context switching between coroutines is far less
expensive than context switching between threads if the coroutines run in the same thread.
However, if they are in different threads (like here), switching coroutine contexts requires an
expensive thread context switch.

Tip
The withContext function is crucial on Android and other UI frameworks to easily switch
between background threads and the UI thread (all UI updates must be done on the UI thread).
Unless you actually want multiple asynchronous calls running in parallel, withContext is often
the better alternative to async-await when expecting back a return value from a suspending
function. For instance, imagine you want to perform a database operation:

suspend fun loadUser(id: Int): User = withContext(DB) { … }

This allows using the natural return type and ensures that the database operation always runs
in the dedicated DB context. You will see several examples of this in Chapters 7 and 8.

Another convenient library function for asynchronous calls is withTimeout, which lets you
easily timeout calls after a specified amount of time. Listing 6.34 shows its basic usage. This code
reaches the print statement twice before timing out.

Listing 6.34 Timeout for Asynchronous Calls


import kotlinx.coroutines.*

runBlocking {
withTimeout(1200) {
repeat(10) {
delay(500)
println("${it + 1} of 10")
}
}
}

Similar to regular cancellation, a timeout is signaled by a TimeoutCancellationException


but unlike a CancellationException, a TimeoutCancellationException gets printed to
stderr and causes the program to stop if it is not caught. That’s because a timeout is not the
expected way for the coroutine to stop. Listing 6.35 shows a safer way to call withTimeout. A
finally block is useful to close any connections opened by the coroutine.
Kotlin Coroutines 177

Listing 6.35 Handling Timeouts


import kotlinx.coroutines.*

runBlocking {
try {
withTimeout(1200) {
repeat(10) {
delay(500)
println("${it + 1} of 10")
}
}
} catch (e: TimeoutCancellationException) {
println("Time is up!")
} finally {
println("Cleaning up open connections...")
}
}

A Coroutine’s Job
Apart from a CoroutineDispatcher, the context contains a Job object that is also worth
exploring in more detail. It can be used to create parent-child hierarchies of coroutines that
facilitate cancellation, and to bind coroutines to the lifecycle of an activity, among other things.
As you know, passing coroutineContext creates a child coroutine. Because it is passed in by
default, using a nested coroutine builder suffices to create a child coroutine. Listing 6.36 shows
how this affects cancellation. It starts ten coroutines of which only three will complete before
being cancelled. This shows that cancelling the parent coroutine cancels its children, and in fact
this goes on recursively.

Listing 6.36 Child Coroutines and Cancellation


import kotlinx.coroutines.*

runBlocking {
val parent = launch {
repeat(10) { i ->
launch { // Implicitly uses coroutineContext, thus parent context
delay(300 * (i + 1))
println("Coroutine ${i + 1} finished") // Only 1, 2, and 3 get to print
}
}
178 Chapter 6 Concurrency in Kotlin

delay(1000)
parent.cancelAndJoin() // Cancels parent coroutine along with all its children
}

Here, cancelling the parent coroutine recursively cancels all its children so that only the first
three coroutines get to print something. The Job also provides access to important properties
of the job execution. The property isCompleted is one of these, and similarly there are also
isCancelled and isActive. Useful functions apart from cancel and join include cancel-
Children to only cancel all child coroutines, joinChildren to wait for the completion of all
the coroutine’s children, and the invokeOnCompletion callback.

In principle, a coroutine may run longer than its associated UI element but still hold references
that prevent garbage collection. For instance, an Android activity (a “screen” in an app) may
have started an asynchronous call just before the user rotates the app, causing the activity to be
destroyed. You can avoid such a memory leak by creating an explicit parent job per activity, which
is used as the parent for every coroutine that should be bound to its corresponding activity. This
approach works for any elements that have a lifecycle and may get destroyed while a coroutine
runs, but we will consider an Android activity in the following. So the goal is to bind a parent job
to an activity and cancel any coroutines that were launched and are still running when that activ-
ity gets destroyed. One approach to implement this is described in the “Guide to UI Programming
with Coroutines.”5 As a first step, you can model the concept of a class that has a parent job
attached to it as an interface, as in Listing 6.37.

Listing 6.37 Binding Coroutines to Activity #1: The JobHolder Interface


import kotlinx.coroutines.*

interface JobHolder { val job: Job }

This interface can then be implemented by any activity that should have coroutines attached to
its lifecycle. The key point is that the activity then calls job.cancel() in its onDestroy method,
which will also cancel all child coroutines of the job, as shown in Listing 6.38.

Listing 6.38 Binding Coroutines to Activity #2: The Activity


import kotlinx.coroutines.Job
import android.support.v7.app.AppCompatActivity

class MainActivity : AppCompatActivity(), JobHolder {

5. https://github.com/Kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md
Kotlin Coroutines 179

override val job = Job()


override fun onDestroy() {
super.onDestroy()
job.cancel()
}
}

With this, if you launch all coroutines with launch(job) { … } or async(job) { … }, you
no longer need to keep track of the coroutines started by the activity. If the activity gets destroyed,
the coroutine cancellation policy will automatically cancel all coroutines attached to the activity.
The guide goes one step further to make a job available from every view. On Android, every view
carries a context that refers to its activity, so this context can be used to access the parent job if
the activity is a JobHolder, as in Listing 6.39.

Listing 6.39 Binding Coroutines to Activity #3: Accessing Job from Views
import android.view.View
import kotlinx.coroutines.NonCancellable

val View.contextJob
get() = (context as? JobHolder)?.job ?: NonCancellable

For every view, this adds an extension property that returns a reference to the parent activity’s job
if it is a JobHolder, or to NonCancellable otherwise. Using launch(contextJob), every view
can start a coroutine that is automatically cancelled if the containing activity is destroyed.

The dispatcher and job are the context elements you will likely use most often, but there are more
predefined context elements that should be mentioned. Specifically, there is CoroutineName,
which is useful for debugging, and CoroutineExceptionHandler, which is called in case an
exception occurs during coroutine execution. You can simply create instances of these and pass
them as the context parameter, as shown in Listing 6.40.

Listing 6.40 CoroutineName and CoroutineExceptionHandler


import kotlinx.coroutines.*

// CoroutineName
val name = CoroutineName("Koroutine")
launch(name) { … }

// CoroutineExceptionHandler
val exceptionHandler = CoroutineExceptionHandler { context, exception ->
180 Chapter 6 Concurrency in Kotlin

println("Crashed with context $context")


exception.printStackTrace()
}
launch(exceptionHandler) { … }

At this point, the observant reader may be wondering how to combine these context elements.
There is only one CoroutineContext parameter, so what if you want a coroutine running on the
UI thread with a specific parent job, or a coroutine with a name and an exception handler, or all
of these? Conveniently, the CoroutineContext class defines an operator function plus, which
allows exactly these combinations. Listing 6.41 shows the operator’s signature.

Listing 6.41 CoroutineContext Combination Operator


public operator fun plus(context: CoroutineContext): CoroutineContext

Because all context elements implement CoroutineContext, each of them work with this opera-
tor. So you can easily put together the desired context for a coroutine as in Listing 6.42.

Listing 6.42 Combining Contexts


// CoroutineName + CoroutineExceptionHandler
launch(name + exceptionHandler) { … }

// Job + CoroutineDispatcher
launch(contextJob + UI) { … } // Uses ‘contextJob’ from Listing 6.39 (Android)

Note that elements from the context on the right-hand side replace all elements with the same
key from the left-hand side. The keys are basically the class names of the elements, meaning you
can access them as shown in Listing 6.43.

Listing 6.43 Accessing Context Elements


import kotlinx.coroutines.*
import kotlin.coroutines.experimental.ContinuationInterceptor

launch(name + exceptionHandler) {
println("Context: ${coroutineContext}")
println("Job: ${coroutineContext[Job]}")
println("Dispatcher: ${coroutineContext[ContinuationInterceptor]}")
println("Name: ${coroutineContext[CoroutineName]}")
println("Exception Handler: ${coroutineContext[CoroutineExceptionHandler]}")
}
Kotlin Coroutines 181

Recall that each coroutine carries a coroutineContext that can be accessed from inside
it. Again, this context is an indexed set of elements. Particular elements from it can be
accessed using the corresponding key. For instance, to retrieve the Job from it, you can
use coroutineContext[Job]. This works because the Job class defines a companion
object Key : CoroutineContext.Key<Job> so that the element access is equivalent to
coroutineContext[Job.Key].

Further Coroutine Parameters


At this point, you have a good understanding of the coroutine context, its elements, and how
to use them. But in addition to the CoroutineContext parameter that every coroutine builder
accepts, launch and async also accept a CoroutineStart parameter. You have briefly encoun-
tered this in the context of JavaScript versus C#-style coroutine scheduling.

As discussed, Kotlin typically schedules a coroutine for later execution (JavaScript-style) but you
can use CoroutineStart.UNDISPATCHED to use the C#-style instead, meaning the coroutine
is run immediately until it reaches its first suspension point. Apart from CoroutineStart.
DEFAULT and CoroutineStart.UNDISPATCHED, there are two more interesting ways to schedule
your coroutines.

The first one is CoroutineStart.LAZY, which starts running the coroutine only when neces-
sary. For launch, this means it can be started explicitly using job.start() or implicitly once
job.join() is called. For async, there’s the additional option to call await to trigger execution.
Note that join causes the coroutine to execute synchronously in Listing 6.44. It’s still nonblock-
ing, but be aware that lazy coroutines may take a while to complete.

Listing 6.44 Starting Coroutines Lazily


runBlocking {
val job = launch(start = CoroutineStart.LAZY) { // Runs only when triggered
println("Lazy coroutine started")
}
println("Moving on...")
delay(1000)
println("Still no coroutine started")
job.join() // Triggers execution of the coroutine using join()
println("Joined coroutine")
}

The last possible value is CoroutineStart.ATOMIC, which guarantees that the coroutine cannot
be cancelled before it at least starts its execution.

The last parameter for coroutine builders (apart from the lambda block) is a separate parent job.
This was introduced in the current version of coroutines and overrides any parent job from the
182 Chapter 6 Concurrency in Kotlin

coroutine context. But you can also use the coroutine context to define the parent job as shown
in this chapter.

You are free to implement your own coroutine builders and dispatchers, although wrappers are
available for the most prominent frameworks and APIs. These should cover most use cases6:

 kotlinx-coroutines-core for basic coroutine support


 kotlinx-coroutines-android for Android development
 kotlinx-coroutines-javafx for JavaFX
 kotlinx-coroutines-swing for Swing
 kotlinx-coroutines-jdk8 for interoperating with CompletableFuture
 kotlinx-coroutines-nio for interoperability with Java’s nonblocking I/O
 kotlinx-coroutines-rx2 for RxJava 2.x support

For instance, if you prefer working with Java’s CompletableFuture and its fluent API, you can
still use coroutines. In kotlinx-coroutines-jdk8, you find a future coroutine builder, which
works similar to async (and similar to CompletableFuture.supplyAsync) but returns a
CompletableFuture instead of a Deferred (see Listing 6.45).

Listing 6.45 Combining Coroutines and CompletableFuture


import kotlinx.coroutines.*
import kotlinx.coroutines.future.*

fun fetchValueAsync() = future { delay(500); 7 } // Uses ‘future’ coroutine builder

runBlocking {
fetchValueAsync().thenApply { it * 6 }
.thenAccept { println("Retrieved value: $it") }
.await()
}

To recap what was covered so far and to give a better intuition and mental model of the world of
suspending functions and how it relates to regular code, Figure 6.3 illustrates their relationships.
As mentioned, coroutine builders allow you to enter the world of suspending functions, suspend-
ing functions keep you there, and calling suspendCoroutine in a suspending function captures
its continuation so that you can work with it as with a callback, effectively taking you back to
regular code.

6. A complete, up-to-date list is provided at https://kotlin.github.io/kotlinx.coroutines/


Kotlin Coroutines 183

Regular World World of Suspending Functions

Coroutine builders
launch /async /runBlocking /future /...
Synchronous
Coroutine
code

Suspending function calls Coroutine /


Suspending fct.

Coroutine /
Suspending fct.

suspendCoroutine
Coroutine /
Callback
Suspending fct.

Figure 6.3 Overview of the relationship between regular code and the “world of suspending
functions,” and the different ways to switch between them

Generators
As mentioned at the beginning of this chapter, coroutines are intended to be general enough to
also incorporate the concepts of async-await, other asynchronous libraries, and generators. At this
point, you know how to write async-await-style code with Kotlin’s coroutines, and you also know
that they can wrap other asynchronous APIs such as Java NIO and other future implementations
such as CompletableFuture. This section introduces generators and explains how they are
implemented using coroutines.

A generator is an iterator that yields values lazily on demand. Although its implementation may
look sequential, execution is suspended until the next value is requested. Values are emitted using
the yield function, similar to a yield keyword in languages such as Python or C#. Another,
more theoretical way to look at generators is that they are less general coroutines in that corou-
tines can yield control to any other coroutine, whereas generators always yield control to their
caller. Listing 6.46 implements the Fibonacci sequence with a generator.

Listing 6.46 Creating a Suspending Sequence (Generator)


import kotlin.coroutines.experimental.buildSequence

val fibonacci = buildSequence {


yield(1)
var a = 0
var b = 1
184 Chapter 6 Concurrency in Kotlin

while (true) {
val next = a + b
yield(next)
a = b
b = next
}
}

fibonacci.take(10).forEach { println(it) } // 1, 1, 2, 3, 5, 8, …

Similar to coroutine builders, buildSequence accepts a suspending lambda but is not a suspend-
ing function itself, and it uses coroutines to compute new values. yield is a suspending function
that suspends execution until the next element is requested so that each call to next causes the
generator to run until it reaches the next yield. Note that you can use control flow structures
naturally within generators as you can inside any suspending function. Also note that build-
Sequence is similar to generateSequence but uses suspending functions and yield, whereas
generateSequence calls the supplied function each time to produce the next value and does
not use coroutines at all.

Note
Generators do not introduce asynchrony, their execution is completely controlled by the caller,
and they run synchronously with their caller. This is one example of synchronous suspending
functions and demonstrates that suspending computation does not necessarily imply asynchro-
ny. You can think of it as no progress being made inside a generator unless it is invoked by a
caller; there is no asynchronous computation taking place. In other words, yield is a suspend-
ing function but not an asynchronous function.
This also explains why Kotlin uses suspend as the keyword and not async as in other languag-
es like C#. Remember: In Kotlin, you have a choice to opt in to asynchrony; it is not implied by
suspend alone.

Aside from buildSequence, Kotlin also offers a buildIterator function. An iterator is almost
the same as a sequence—in fact, sequences in Kotlin just wrap iterators. In contrast to iterators,
they can typically be iterated multiple times, and they are stateless. In other words, each iteration
over a sequence gives you a fresh iterator. Listing 6.46 showed a sequence that allows traversing its
iterator multiple times: You could take the first five elements from fibonacci again do to some-
thing with them. Other sequence implementations only allow accessing their iterator once (see
ConstrainedOnceSequence).

One more concept that can be explained well by means of generators is the @Restricts
Suspension annotation. In the standard library, it is used on the SequenceBuilder class,
which is the receiver of the lambda passed to buildSequence and buildIterator (see
Listing 6.47). So inside that lambda you have access to methods from SequenceBuilder,
which is where yield and yieldAll come from.
Kotlin Coroutines 185

Listing 6.47 Declaration of buildSequence


fun <T> buildSequence(block: suspend SequenceBuilder<T>.() -> Unit): Sequence<T>

Now imagine you are the library developer for SequenceBuilder. You must make sure that all
suspension points are exclusively from yields when using that class. In other words, you must
restrict library users so that they cannot add new ways to suspend the coroutine via extension
functions. This is what @RestrictsSuspension in the declaration of SequenceBuilder does
(see Listing 6.48).

Listing 6.48 Declaration of SequenceBuilder with Restricted Suspension


@RestrictsSuspension
public abstract class SequenceBuilder<in T> { … } // Declaration from stdlib

Because of this annotation, suspending extension functions must call either another suspend-
ing extension or a suspending member function of SequenceBuilder. This way, all suspending
extensions end up calling a suspending member function—in this case, yield or yieldAll.

Actors and Channels


At the outset of this chapter, I briefly mentioned the actor model for concurrent programming.
In this model, an actor represents a unit of concurrent computation that is decoupled from other
actors. In particular, actors share no mutable state. Instead, they communicate only via message
passing. To receive messages, each actor has a message channel attached to it. Based on incom-
ing messages, the actor decides what to do—spawn more actors, send messages, or manipulate
its private state. The key point is that there is no chance for race conditions and thus no need for
locks when there is no shared state.

Note
In the original actor model described by Carl Hewitt, there are no channels.7 Actors communi-
cate directly, and a channel would be another separate actor because, in the model, everything
is an actor. However, programming languages that incorporate actors such as Erlang, Elixir, Go,
or Kotlin do not strictly follow this original model.

More generally, actors can have a many-to-many relationship to channels so that a single actor
may read from multiple channels. Likewise, multiple actors may read messages from the same
channel to distribute work. Although this is a model of concurrency, actors themselves work
sequentially. If an actor receives three messages, it will process them one after the other. The
power of the model originates from a system of actors working in parallel. Concurrency is facili-
tated by communicating only via message passing.

7. Hewitt talks about his concept here: https://youtu.be/7erJ1DV_Tlo


186 Chapter 6 Concurrency in Kotlin

The actor model has affected many programming languages. For example, it is a fundamental
concept in Erlang,8 the Ericsson language, because its originators needed a model for fault-toler-
ant distributed systems. Consequently, actors are also essential in Elixir,9 a modern language that
builds on Erlang. The Go programming language,10 developed by Google, is also directly influ-
enced by the actor model. In Go, actors are implemented using “goroutines,” which are closely
related to Kotlin’s coroutines.11 Accordingly, you can implement actors with coroutines in Kotlin.
Listing 6.49 presents a simple actor that can receive a string message and print it. The code prints
"Hello World!" when run.

Listing 6.49 A Simple Actor


import kotlinx.coroutines.channels.actor
import kotlinx.coroutines.runBlocking

val actor = actor<String> {


val message = channel.receive()
println(message)
}

runBlocking {
actor.send("Hello World!") // Sends an element to the actor’s channel
actor.close() // Closes channel because actor is no longer needed
}

To create a new actor, Kotlin provides an actor function, which is in fact another coroutine
builder—remember that actors are coroutines in Kotlin, so creating an actor means creating a new
coroutine. The actor tries to receive a message from its channel, which is accessible inside the
lambda from its receiver type ActorScope. From the main thread, a string is sent to the actor’s
channel and after that, it is closed. There are several things to note here:
 Both send and receive are suspending functions; send suspends execution as long as the
channel is full, and receive suspends execution as long as the channel is empty.
 For that reason, the main function must be wrapped with runBlocking to be allowed to
call the suspending send.
 Calling close does not immediately stop the actor coroutine. Instead, the mental model is
that close sends a special “close token” to the channel, and the channel is still read first-
in-first-out (FIFO) so that all previous messages are processed before the channel is actually
closed.

8. https://www.erlang.org/
9. https://elixir-lang.org/
10. https://golang.org/
11. In fact, you can easily simulate them using fun go(block: suspend () -> Unit) = CommonPool.
runParallel(block)
Kotlin Coroutines 187

 The actor above terminates after printing the first message, therefore the channel is closed.
Trying to send another message to it causes a ClosedSendChannelException. Thus,
calling actor.close is actually redundant in Listing 6.49. This is not typical behavior
for an actor; a more real-world way to implement an actor is shown in Listing 6.50, which
prints "Hello" on one line and then "World" on the next.

Listing 6.50 Actor that Keeps Reading from its Channel


import kotlinx.coroutines.channels.actor
import kotlinx.coroutines.runBlocking

// This actor keeps reading from its channel indefinitely (until it’s closed)
val actor = actor<String> {
for (value in channel) { println(value) }
}

runBlocking {
actor.send("Hello") // Makes actor print out "Hello"
actor.send("World") // Makes actor print out "World"
actor.close()
}

The crucial part is the for loop, which keeps the actor alive until its channel is closed. Now, you
can send an arbitrary number of messages to the actor and then finally call close when the actor
is no longer needed.

The actor function returns a SendChannel so that you are only allowed to send messages to the
actor but not receive messages from the channel (only the actor should do that). Inside the actor
lambda, the channel has type Channel<E>, which implements both SendChannel<E> and
ReceiveChannel<E> so that you can call both send and receive inside the lambda. However,
when sending to itself, an actor must make sure the channel is not full. Otherwise, it will suspend
indefinitely while waiting for a receive and effectively produce a deadlock. So you should
check if channel.isFull beforehand, and you must also make the channel buffer at least one
element—this is easily achieved by setting the capacity as shown later in this section.

Note
Kotlin’s channels are fair in the sense that they follow the FIFO pattern, meaning elements that
are sent first are also received first.

As a coroutine builder, the actor function is very similar to the ones you have already seen. It
accepts a CoroutineContext, a CoroutineStart, an explicit parent Job, and the suspend-
ing lambda that defines its behavior. These work the same way as before. A lazily started actor
becomes active once an element is first sent to its channel.
188 Chapter 6 Concurrency in Kotlin

In addition to these parameters, actors have an additional capacity that defines how many
elements its channel can buffer. By default, the capacity is zero, meaning that send must suspend
until a receive is issued, and vice versa. This is called a RendezvousChannel because the
sending and receiving sides must “meet” at one point in time. In Listing 6.51, the receive is
implicitly called in the for loop and suspends until a new element is sent to the channel.

Another useful type of channel is a conflated channel that only keeps the last element sent to it.
This is often the desired behavior, for instance, to update a UI only with the most recent data.

Listing 6.51 Actor with Conflated Channel


import kotlinx.coroutines.channels.*
import kotlinx.coroutines.*

runBlocking {
val actor = actor<String>(capacity = Channel.CONFLATED) { // Conflated channel
for (value in channel) { println(value) }
}

actor.send("Hello") // Will be overwritten by following element


actor.send("World") // Overwrites Hello if it was not yet consumed by the actor
delay(500)
actor.close()
}

This example sets the capacity parameter to Channel.CONFLATED so that send no longer
suspends until a receive is issued. Instead, the new value just overrides the old one so that only
the most recent value is retrieved with receive. Note that, without the delay, the code will likely
not print anything because then the close token is the last one when the actor gets to receive a
message for the first time.

Apart from these, you can use Channel.UNLIMITED to create a channel with unlimited capacity
(using a LinkedListChannel) or use actor(capacity = n) to use a channel with a specific
buffer size n (using ArrayChannel). As you can imagine, the former uses a linked list to buffer
elements, whereas the latter uses an array. With unlimited capacity, the sender never suspends.

Tip
I recommend reading the source code documentation in the Kotlin standard library. It pro-
vides detailed info about most entities. Ctrl+Q (Ctrl+J on Mac) to show quick docs and Ctrl+B
(Cmd+B on Mac) to jump to declaration are your best friends for this in Android Studio and
IntelliJ.
Jumping to declaration has the added benefit that you can explore the actual implementation.
For instance, you’ll notice that all coroutine builders are implemented in a very similar way.
Kotlin Coroutines 189

Next, you can create actor coroutines that use multiple channels and receive their next message
from whichever channel first has an element. Listing 6.52 implements an actor that reads
numbers from two channels simultaneously using a select expression (as in Go). It explicitly
creates the two channels, therefore there’s no need for the actor coroutine builder. Any corou-
tine that selects from these two channels qualifies as an actor here.

Because receive only allows reading from one channel at a time, this code uses the onReceive
clause. There are other such clauses available for use inside a select such as onSend, onAwait,
onLock, onTimeout, and onReceiveOrNull. Internally, all these implement a SelectClause
interface, and they can be combined arbitrarily inside a select to do something with the value
that is first available—whether it’s a deferred result, a timeout, a lock acquired, or a new message
on a channel. In other words, whatever event occurs first will be “selected.”

Listing 6.52 Actor with Multiple Channels


import kotlinx.coroutines.channels.*
import kotlinx.coroutines.*
import kotlinx.coroutines.selects.select

runBlocking {
val channel1 = Channel<Int>()
val channel2 = Channel<Int>()

launch { // No need for the actor coroutine builder


while (true) {
select<Unit> { // Provide any number of alternative data sources in here
channel1.onReceive { println("From channel 1: $it") }
channel2.onReceive { println("From channel 2: $it") }
}
}
}
channel1.send(17) // Sends 17 to channel 1, thus this source is selected first
channel2.send(42) // Sends 42 to channel 2, causing channel 2 to be selected next
channel1.close(); channel2.close()
}

Note
The select function is biased to the first clause, meaning that if multiple clauses are select-
able at the same time, the first one will be selected. You can use this intentionally, for instance,
to send messages to a primary channel while it’s not full and to a secondary channel otherwise.
In case you explicitly don’t want this behavior, you can use selectUnbiased instead. This ran-
domly shuffles the clauses each time before selecting, making the selection fair.
190 Chapter 6 Concurrency in Kotlin

Similarly, you can make multiple actors send to the same channel, as in Listing 6.53. Here, the
used channel is simply a local variable so that it is accessible by all actors. Three producers are
launched, all of which try sending a message to the channel repeatedly and suspend until there
is a receiver (this is a rendezvous channel). On the consumer side, consumeEach can be used as an
alternative to an explicit for loop. Multiple coroutines that read from the same channel can be
implemented the same way. You just have to provide access to the same channel object.

Listing 6.53 Multiple Actors on Same Channels


import kotlinx.coroutines.channels.*
import kotlinx.coroutines.*

runBlocking {
val channel = Channel<String>()
repeat(3) { n ->
launch {
while (true) {
channel.send("Message from actor $n")
}
}
}
channel.take(10).consumeEach { println(it) }
channel.close()
}

In the previous examples, there are several coroutines that don’t behave like actors in the clas-
sical sense because they are not associated with an incoming channel but rather with an outgo-
ing channel. We shall call these producers. For instance, Listing 6.53 creates three producers.
Intuitively, producers are the counterparts to actors because they have an outgoing channel
attached.

Kotlin provides a more convenient way to create such producers, shown in Listing 6.54. Here, the
produce coroutine builder creates a producer. Accordingly, it returns a ReceiveChannel because
you’re only supposed to receive the producer’s messages from the outside, whereas sending is left
to the producer.

Listing 6.54 Creating a Producer


import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*

runBlocking {
val producer = produce {
Kotlin Coroutines 191

var next = 1
while (true) {
send(next)
next *= 2
}
}
producer.take(10).consumeEach { println(it) }
}

Note that this producer emits a potentially infinite stream of values. It is only decelerated by the
fact that it uses a RendezvousChannel so that send suspends until there’s a receiver on the other
end. Without the take(10) restriction, the program would keep printing powers of two until the
integer overflows.

To use more interesting message types than just Int or String, you can either use any other
predefined type or define a custom message type. Sealed classes are a good fit for this use case to
create a restricted class hierarchy of possible message types, as done in Listing 6.55.

Listing 6.55 Actor with Custom Message Type


sealed class Transaction // Uses a .kt file to be able to declare sealed class
data class Deposit(val amount: Int) : Transaction()
data class Withdrawal(val amount: Int) : Transaction()

fun newAccount(startBalance: Int) = actor<Transaction>(capacity = 10) {


var balance = startBalance
for (tx in channel) {
when (tx) {
is Deposit -> { balance += tx.amount; println("New balance: $balance") }
is Withdrawal -> { balance -= tx.amount; println("New balance: $balance") }
}
}
}

fun main(args: Array<String>) = runBlocking<Unit> { // Not a script (see first line)


val bankAccount = newAccount(1000)
bankAccount.send(Deposit(500))
bankAccount.send(Withdrawal(1700))
bankAccount.send(Deposit(4400))
}
192 Chapter 6 Concurrency in Kotlin

Here, the actor acts as a bank account that receives transactions as messages. A transaction can
either be a deposit or a withdrawal, as defined by the sealed class. The account can buffer up to
ten transactions; further transactions are suspended until the account has a chance to catch up.

Note
Although coroutines and the actor model may seem like modern concepts, they are not. The
term coroutine goes back all the way to 1963 and was first used by Melvin E. Conway to refer
to a generalization of a subroutine that can yield control and resume later.12 Similarly, as men-
tioned, the actor model was introduced by Carl Hewitt back in 1973.13
Remember that Kotlin does not claim to be innovative but rather an amalgamation of well-
known and proven concepts that are suitable to develop large-scale software. What is unique
about coroutines, however, is how they allow writing asynchronous code in sequential style.

Concurrency Styles
In order to recap the differences between coroutine-based, callback-based, and future-based
concurrency, it is a useful exercise to explore how to transform between them. This way, it
becomes clear that all these are ultimately just fancy callbacks, and if you understand just one of
these approaches and the transformation between them, you understand them all.

Listing 6.56 demonstrates this transformation via three function signatures that represent the
same purpose—asynchronously fetching an image.

Listing 6.56 Transformation between Concurrency Styles


// Callbacks
fun fetchImageAsync(callback: (Image) -> Unit) { … }

// Futures
fun fetchImageAsync(): Future<Image> { … }

// Coroutines
suspend fun fetchImage(): Image { … }

In the case of futures, the callback is defined separately, typically via combinators like thenCom-
pose or thenAccept, but it does still use callbacks. These define what happens after the value
becomes available. In the case of coroutines, Kotlin hides the callback (more precisely, the contin-
uation) but the compiler passes an additional Continuation parameter to every suspending
function. This continuation represents which operations are left to do after suspension—in other
words, it’s a callback. Intuitively, at every suspension point, the corresponding continuation

12. http://www.melconway.com/Home/pdf/compiler.pdf
13. https://www.researchgate.net/scientific-contributions/7400545_Carl_Hewitt
Kotlin Coroutines 193

contains the code that follows the suspension point in the function—because that is what’s left to
do after resuming at that suspension point. As an example, refer back to Listing 6.7, in which the
suspension points are highlighted.

Coroutines in Practice
To facilitate getting started with coroutines in your project and to overcome the likely roadblocks,
this section discusses practical issues like debugging, interoperability with Java, and interoperabil-
ity with asynchrony libraries.

Debugging Coroutines
Concurrency is hard, and debugging concurrent code can be excruciating. To ease the pain, this
section discusses practices for debugging coroutines. However, also keep in mind that concur-
rency becomes a lot more predictable when avoiding shared mutable state and using immutable
data structures. Consider these practices if you find yourself debugging your concurrent code a lot
due to synchronization issues.

One well-known way to monitor program execution is logging. In concurrent code, it’s a good
idea to add the name of the current thread to the log so that each logged operation can be
assigned to its corresponding thread, as is done in Listing 6.57. However, there may be thousands
of coroutines running on the same thread so that context is not sufficient. Luckily, if you use
the compiler flag -Dkotlinx.coroutines.debug, Kotlin automatically appends the current
coroutine’s ID to the thread name. That way, a log item produced in Listing 6.57 would start with
[main @coroutine#1] instead of just [main].

Listing 6.57 Logging Current Thread (and Coroutine)


fun log(message: String) = println("[${Thread.currentThread().name}] $message")

The name suffix always has the form “@name#id” and uses the CoroutineName and
CoroutineId from the coroutine context. You can set a custom CoroutineName, but
CoroutineId is a private class used by Kotlin. The default name is simply “coroutine” and the
ID is incremented by one for each newly created coroutine. To pass the debug flag automatically
when running your application in IntelliJ, you can create a run configuration (Ctrl+Shift+A /
Cmd+Shift+A, and type in “edit config”) with -Dkotlinx.coroutines.debug flag added as a
VM option.

In Android Studio, you cannot create such a run configuration. Instead, you can create an
Application class that sets up the system property as in Listing 6.58. This class is automatically
instantiated before any activity and thus suitable to set up such configuration. It is generally used
to contain the global state of an Android app. To wire it into your project, you must specify it as
the android:name inside the <application> in your manifest file.
194 Chapter 6 Concurrency in Kotlin

Listing 6.58 Enabling Coroutine Debugging on Android


class App : Application() {

override fun onCreate() {


super.onCreate()
System.setProperty("kotlinx.coroutines.debug",
if (BuildConfig.DEBUG) "on" else "off")
}
}

// In AndroidManifest.xml
<application
android:name=".App" …>
</application>

Besides this particular feature, you are free to use debugging facilities as usual in coroutine code:
breakpoints, variable watchers, thread overview, frames, and more. When placing breakpoints
in IntelliJ or Android Studio, you can decide whether that breakpoint should stop just the thread
that reaches it or all threads. This is important to keep in mind if a breakpoint doesn’t behave as
you would expect.

Wrapping Asynchronous APIs


Imagine you want to work with a future implementation for which there are no wrappers avail-
able yet—and you want to use them idiomatically in Kotlin. For this, you can write your own
coroutine builder and custom await function. This way, you basically map from futures to the
world of suspending functions. To dissect the structure of a coroutine builder, consider the imple-
mentation of the future coroutine builder that wraps Java 8’s CompletableFuture. Listing 6.59
shows a simplified version of its implementation.

Listing 6.59 Implementation of future Coroutine Builder (Simplified)


public fun <T> future(…): CompletableFuture<T> {
// Set up coroutine context… (omitted)
val future = CompletableFutureCoroutine<T>(newContext + job)
job.cancelFutureOnCompletion(future)
future.whenComplete { _, exception -> job.cancel(exception) }
start(lambda, receiver=future, completion=future)

return future
}

private class CompletableFutureCoroutine<T>(…) : … {


// …
Kotlin Coroutines 195

override fun resume(value: T) { complete(value) }


override fun resumeWithException(exception: Throwable) {
completeExceptionally(exception)
}
}

The future coroutine builder first sets up the coroutine context for the new coroutine. Then it
creates a new coroutine of the specialized type CompletableFutureCoroutine. It also tells the
coroutine to cancel the future on completion—this forwards cancellation of the coroutine to the
underlying future. After that, it sets things up the other way around so that the coroutine job is
cancelled when the future completes. With this, the future is ready to go so the function starts
the coroutine by running the given lambda with the newly created future as its receiver type and as
its completion. For this, it invokes the given CoroutineStart object with the block of code, the
receiver, and the completion.

Concerning the receiver, you may be wondering how the receiver type of the lambda can be
changed even though you implement the lambda with CoroutineScope as its receiver. This
works because CompletableFutureCoroutine also implements CoroutineScope so that this
remains a valid receiver. Regarding the completion: This is the continuation that is invoked
when the coroutine completes. Here it becomes very apparent that both futures and continu-
ations are essentially just callbacks, and therefore map well onto each other. Specifically,
complete maps to resume and completeExceptionally maps to resumeWithException. All
these methods define what’s supposed to happen when the asynchronous call finishes—in other
words, when the supplied lambda terminates.

In this case, what happens is that either CompletableFutureCoroutine.complete is


invoked with the result value or completeExceptionally is invoked if the lambda produced
an exception. This populates the future with the computed result (or exception) and it makes
that result available to the outside. The coroutine itself is the CompletableFuture that is
returned. For example, a user may hold a reference to the returned future, which is internally
a CompletableFutureCoroutine, but they only see it as a CompletableFuture due to
the return type. So when the user calls join on the future, it waits until either complete or
completeExceptionally is called—which is the same as waiting for the coroutine to finish.

Adding your custom await function for the future is simpler because no new coroutine has to be
created. The main task is to map the functions appropriately, similar to the way it is done in the
CompletableFutureCoroutine. Listing 6.60 shows the basic structure of a custom await function.
The key point here is suspendCancellableCoroutine, which is a low-level primitive to suspend
execution. This captures the current state in a Continuation object and passes that continuation to
the lambda block. This is what allows the code to resume at a later point—all local state is captured in
the continuation. As always, the continuation also defines what happens after the suspension. This
is expressed in the lambda; accordingly, its type is (Continuation<T> -> Unit).

In Listing 6.60, the continuation waits for the future to complete and resumes the coroutine
normally or with exception, depending on whether an exception occurred. If you look at the
actual implementation of Kotlin’s CompletableFuture.await, you will see that it performs
additional optimizations. But this listing presents the basic structure to be followed by any
196 Chapter 6 Concurrency in Kotlin

custom await function. The key takeaway is that every future typically implements a normal and a
failure case, which are mapped to resume and resumeWithException, respectively.

Listing 6.60 Custom Await Function (Simplified)


import kotlinx.coroutines.future.future
import kotlinx.coroutines.*
import java.util.concurrent.CompletableFuture

suspend fun <T> CompletableFuture<T>.myAwait(): T {


return suspendCancellableCoroutine { continuation ->
whenComplete { value, exception ->
if (exception == null) continuation.resume(value)
else continuation.resumeWithException(exception)
}
}
}

runBlocking {
val completable = future { delay(1000); 42 }
println(completable.myAwait()) // 42

val fail = future<Int> { throw Exception("Could not fetch value") }


println(fail.myAwait()) // Throws exception
}

As an example, you can use the shown approach in order to work with Retrofit’s Call<T> in an
idiomatic way, meaning you could create a call { … } coroutine builder with a custom await
function defined as an extension on Call<T>. However, even for futures without an official
wrapper, you can often find wrappers implemented by the community.

Note
Coroutines themselves only use heap resources and no native resources. State is captured in
continuation objects, which are automatically garbage-collected when there are no more refer-
ences to them. This facilitates resource management with coroutines. You only have to make
sure to close resources that you explicitly opened with a coroutine (or in the case that the
coroutine gets cancelled).

Interoperability
If you try calling a suspending function from Java, it turns out to not be so easy. Why? Remember
that the compiler adds an additional Continuation parameter to every suspending function
and that it provides this automatically in Kotlin code. However, in Java, you would have to pass
a continuation yourself, which is hardly realistic and should never be done because it is highly
Kotlin Coroutines 197

prone to errors and produces unreadable code. Instead, add an asynchronous implementation for
every suspending function that should be callable from Java, as done in Listing 6.61.

Listing 6.61 Wrapping Suspending Function for Java Interop


import kotlinx.coroutines.future.future

suspend fun fetchScore(): Int { … } // This can hardly be called directly from Java
fun fetchScoreAsync() = future { fetchScore() } // This is to be used from Java

// Java
CompletableFuture<Integer> future = fetchScoreAsync();
int asyncResult = future.join();

Remember that the future coroutine builder is from the kotlinx-coroutines-jdk8 library.
Because it returns a CompletableFuture, the result can be processed in Java as usual. async is
not suitable for this because it returns a Deferred with its suspending await function that again
requires a Continuation. This is why coroutines in general are hardly usable from Java—corou-
tines become powerful only in conjunction with suspending functions, and those can barely be
called from Java. This goes back to the fact that suspending functions really are a Kotlin concept
and there is no equivalent to map them to in Java.

Under the Hood


I briefly mentioned that suspending functions are a pure compiler feature. So, from the compiler’s
view, what is the difference between a normal function and a function with suspend modifier?
How is it treated differently to realize this world of suspending functions you’ve explored in this
chapter? It is easiest to grasp the internal mechanisms by looking at a concrete example. So, the
following discussion is based on the suspending function in Listing 6.62.

Listing 6.62 Suspending Function to be Examined


suspend fun suspending(): Int {
delay(1000) // Suspension point #1
val a = 17
otherSuspend() // Suspension point #2
println(a)

return 0
}

As you know, the compiler implicitly adds a continuation parameter to every suspending func-
tion. Thus, the compiled signature in the Java bytecode looks as in Listing 6.63. Note that this is
198 Chapter 6 Concurrency in Kotlin

a transformation to CPS because the continuation is now passed explicitly (imagine this transfor-
mation applied to all suspending functions).

Listing 6.63 Step 1: Adding Continuation Parameter


suspend fun suspending(c: Continuation<Int>): … { … }

Note that the original return type is now captured as the generic type parameter of the continu-
ation, here Continuation<Int>. This indicates the “return type” of the continuation. More
precisely, it becomes the type accepted by its resume function, which in turn passes along its
value to the last suspension point. The function name resume indicates the intuition: It is the
value that the program resumes with after the continuation finishes. You can think of this behav-
ior as a return value of the continuation if this intuition helps you.

This transformation enables the next step: removing the suspend modifier as in Listing 6.64.
Suspension is handled inside the method by compiler-generated code as discussed in Step #4
(Listing 6.66), and resumption is explicit with the passed continuation.

Listing 6.64 Step 2: Removing suspend Modifier


fun suspending(c: Continuation<Int>): … { … }

Next, the return type changes. Irrespective of the original return type, the compiler changes
the return type to Any? as in Listing 6.65. This return type actually represents the union of the
original return type and the special COROUTINE_SUSPENDED type (but Kotlin has no proper
union types14). Using the original return type allows the suspending function to not suspend and
instead to return a result directly (for example, if an asynchronous result is already available). In
contrast, COROUTINE_SUSPENDED indicates that the function did indeed suspend execution.

Listing 6.65 Step 3: Changing Return Type to Any?


fun suspending(c: Continuation<Int>): Any? { … }

At this point, you may be wondering what the compiler passes in as a continuation. Basically,
the continuation represents the rest of the execution. For instance, Listing 6.62 produces three
continuations.
 The initial continuation represents the entire function with its five lines of code.
 The continuation for delay (at suspension point #1) contains the four subsequent lines
because that represents the rest of the computation.
 At suspension point #2, the continuation contains only the two lines of code that remain
after it. Additionally, it captures the current value of a so that, upon resumption, the value
is still available. This is why you can also think of continuations as objects that capture the
state of a coroutine at a suspension point.

14. Interesting discussion about union types: https://discuss.kotlinlang.org/t/union-types/77


Kotlin Coroutines 199

In general, you get one continuation for every suspension point, plus one for the suspend func-
tion itself (thus three in this case). Note that this example demonstrates that, in direct-style
sequential code, continuations are implicitly defined by the sequence of code lines—without
explicitly passing around continuations.

The question becomes how the compiler provides the correct continuations for all three suspending
calls. One solution would be to transform the code into a callback-based style similar to Listing 6.1
at the beginning of this chapter. However, this is not how it is done. Instead, the compiler gener-
ates a state machine for each suspending function. In this state machine, each suspension point is
a state, and state transition is achieved by executing the code until the next suspension point. This
state machine allows the compiler to pass the right continuation to every suspending function. To
do so, it creates a coroutine object that implements Continuation and stores both the program
context (like local variables) and the current state of the state machine. This is then used as the
continuation for every suspending call. One advantage of this is that the state machine object can
be reused for all suspending calls in the function. So it requires only one state machine object to
be instantiated and is therefore efficient. Listing 6.66 shows this approach in a slightly simplified
version; the actual implementation also handles cancellation and other cases.

Listing 6.66 Step 4: State Machine (Simplified)


fun suspending(c: Continuation<Int>): Any? {

val stateMachine = object : CoroutineImpl(…) // Implements Continuation

when (stateMachine.state) { // Three cases for three suspension points (states)


is 0 -> {
stateMachine.state = 1 // Updates current state
delay(1000, stateMachine) // Passes state machine as continuation
}
is 1 -> {
val a = 17
stateMachine.my$a = a // Captures variable value
stateMachine.state = 2 // Updates current state
otherSuspend(stateMachine) // Passes state machine as continuation
}
is 2 -> {
val a = stateMachine.my$a // Recovers value from context
println(a)
return 0
}
}
}
200 Chapter 6 Concurrency in Kotlin

At first, the stateMachine object represents the initial continuation—the one that contains
the whole function body. Thus, it starts off in state zero and executes until the first suspension
point. Before it reaches that suspension point, it updates the current state to reflect the progress
and to resume with the correct continuation. So after delay, the continuation resumes in state
one, thus proceeding to set the value of a. Right after every such assignment of a variable that is
required again later, the value is captured in the state machine. Similarly, before the variable a is
used in state two, its value is restored from the continuation.

With this, you now have a firm understanding of the inner workings of coroutines, in addition
to the practical knowledge of how to use them. Although it is not required to use coroutines in
practice, a good conceptual model of the foundations is useful to understand how things actually
work and therefore how to resolve issues that come up during development.

Summary
In this chapter, you dove deep into Kotlin’s coroutines, the principles on which they are based,
their use cases, and how they are implemented. I consider this to be a more advanced topic and
reckon that it is important to recap the main points. I would also like to highlight that using
coroutines is ultimately very convenient compared with other asynchrony approaches. The main
takeaways that you should remember are the following.
 Concurrency is not the same as parallelism.
 Coroutines are conceptually very lightweight threads—but they’re not really threads; you
may have millions of coroutines per thread.
 Kotlin coroutines are generic enough to cover the use cases of async-await and futures,
generators, parallelism, lazy sequences, actors, Go-like channels and select, and the
producer-consumer pattern—all in a natural way.
 With coroutines, you can write asynchronous code in familiar sequential style including all
control flow constructs, and with natural function signatures.
 Coroutines are powered by suspending functions. These are functions with the additional
superpower to suspend execution at well-defined points (called suspension points).
 Coroutine builders are how you enter the world of suspending functions. They create a new
coroutine that executes a given suspending lambda.
 runBlocking is used for main and test functions to bridge the gap between normal and
suspending worlds.
 launch is used for “fire-and-forget” tasks without return value.
 async is used for asynchronous tasks with a return value to await.
 withContext allows switching between coroutine contexts and can be a good
alternative to async for calls that return a result.
 future is an interoperability coroutine builder for Java 8’s CompletableFuture.
Summary 201

 actor is used to create a coroutine with an attached incoming channel.


 produce is used to create a coroutine with an attached outgoing channel.
 buildSequence and buildIterator are used to create generators.
 There are libraries for Android and other UI frameworks to facilitate concurrency.
 Continuations are essentially callbacks but can also store the state of a coroutine at a
suspension point.
 Coroutines are only attached to a specific thread during execution. While suspended, they
are independent of any threads and may use a different thread when resumed.

Note that Kotlin’s focus on immutability is especially crucial for concurrent code.
Synchronization problems arise from shared mutable state, not shared immutable state. Similarly,
private mutable state is unproblematic. Since coroutines themselves execute sequentially, you can
use mutable data structures within a coroutine without evoking synchronization issues. Only
when sharing state between coroutines must you use immutable or concurrent data structures to
avoid synchronization problems.

Coroutines are a powerful tool. Investing the time to understand how they work pays off in
reduced development time and more stable asynchronous code. Even without understanding all
the internal workings of coroutines, they are convenient to use in practice and allow for more
natural code.

This chapter introduced some of the inner workings as well because it helps to take part in
community discussions, understanding the further development, and potential issues in your
code. Covering the topic in full would require a whole book, but this chapter gives you the tools
you need to understand and use coroutines effectively. In the next two chapters, you will put
coroutines into practice while developing two Android apps.
This page intentionally left blank
II
Kotlin on Android
This page intentionally left blank
7
Android App Development
with Kotlin: Kudoo App

Whether you think you can,


or you think you can't—you're right.
Henry Ford

After publication, the online code was updated for Kotlin 1.3. Go to informit.com/
title/9780134854199 and click on the Downloads tab for more information.

This chapter first explains how to set up Kotlin projects for Android and proceeds to imple-
ment a simple to-do list app called Kudoo. This app uses Android Architecture Components, a
RecyclerView, and other fundamental Android components so that you can learn how to use
these with Kotlin.

Setting Up Kotlin for Android


This section guides you through the required setup for your first Kotlin Android app. You’ll learn
what changes are made to the Gradle build configuration files and how you can use Android
Studio effectively to set up Android projects using Kotlin.

Using Kotlin in Android Studio


Kotlin comes preinstalled since Android Studio version 3, in accordance with Google’s statement
to officially support Kotlin as a language on Android. For you, this means there’s no need to
install the Kotlin plugin separately anymore.

Auto-Generated Gradle Configuration


Before diving into the Gradle configuration, notice that your Android project has two different
Gradle build scripts, both called build.gradle. To follow along in this chapter and Chapter 8,
206 Chapter 7 Android App Development with Kotlin: Kudoo App

Android App Development with Kotlin: Nutrilicious, you must be able to differentiate between
the two scripts.
 First, there is the project’s build.gradle file. This one resides in the project’s root
directory. In Android Studio, you can find it in the Android project view that is located
on the left-hand side of the IDE by default.
 More important, the module’s build.gradle file is the one you will be changing
frequently in the following two chapters. It’s located inside the app directory under the
project root. In Android Studio’s project view, which shows all Gradle scripts in one place,
you can find it under the module’s build.gradle file, as shown in Figure 7.1.

Project’s Build Script


Module’s Build Script

Figure 7.1 Gradle build scripts

When creating a new project in Android Studio 3, Kotlin is automatically configured and acti-
vated. For existing Java projects, you can invoke Android Studio’s action Configure Kotlin in Project
in order to perform the necessary changes.

First, it adds the kotlin-gradle-plugin dependency to your project’s Gradle build file. This
plugin is responsible for compiling your Kotlin sources. With this, the project’s build file has a
structure as in Listing 7.1.

Listing 7.1 Project’s build.gradle File


buildscript {
ext.kotlin_version = "1.2.60" // Adjust to your current version
// …
dependencies {
// …
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
// …

Second, it includes the Kotlin standard library in your module’s Gradle build file to make it avail-
able inside that module. Third, it applies the required plugin kotlin-android to target Android
during build.
Setting Up Kotlin for Android 207

In the current version of Android Studio 3, the plugin kotlin-android-extensions is also


included by default, even though it is not required to build a Kotlin project targeting Android.
However, the Kotlin Android Extensions are extremely useful, so I recommend you use them, and
they will be used in both apps presented in this book. The module’s resulting Gradle build file
should look similar to Listing 7.2.

Listing 7.2 Module’s build.gradle File


apply plugin "com.android.application"
apply plugin "kotlin-android"
apply plugin "kotlin-android-extensions" // Recommended

...

dependencies {
...
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
}

Tip
With Android Studio’s default key bindings, you can use Ctrl+Shift+A (or Cmd+Shift+A on Mac)
to invoke the Find Action command. From there, start typing the name of the action you want to
perform, for instance, “configure kotlin,” and select the desired action.1
You can further improve your workflow by making use of fuzzy search in Android Studio so
that you only need to type the first letter of each word to find the action, for instance, “ckip”
for Configure Kotlin in Project. This same scheme can be used in any search panel in Android
Studio (and IntelliJ IDEA).

Adapting Your Gradle Configuration


If you want to separate your Kotlin files into a directory, such as src/main/kotlin, you’ll need
to add the code from Listing 7.3 to your module’s build.gradle file to make this directory
known to Android Studio for indexing. Alternately, you can place your Kotlin files alongside your
Java files under src/main/java, which is what we do in this book.

1. https://developer.android.com/studio/intro/keyboard-shortcuts
208 Chapter 7 Android App Development with Kotlin: Kudoo App

Listing 7.3 Add Separate Kotlin Sources Directory to Gradle (Optional)


android {
...

sourceSets {
main.java.srcDirs += "src/main/kotlin"
}
}

In case you don’t need to target JDK 6 with your app but rather JDK 7 or 8, you can use the corre-
sponding specialized dependency shown in Listing 7.4 instead of the one in Listing 7.2. These add
additional extensions for APIs introduced in JDK 7 or 8. The targeted JDK version can be changed
under Kotlin Compiler in the Android Studio settings.

Listing 7.4 Dependencies for Targeting JDK 7 or 8


implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" // JDK 7
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" // JDK 8

Similarly, if you want to use Kotlin’s reflection or testing APIs, you must add the appropriate
dependencies from Listing 7.5.

Listing 7.5 Dependencies for Kotlin Reflection and Test Libraries


implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"

Other dependencies can be added in the same way. But this covers all essential Kotlin-related
dependencies you may want to use for Android.

Using Annotation Processors


If you use libraries that contain annotation processors, like Dagger for dependency injection,
the Kotlin plugin must be configured to make them work with Kotlin classes. As shown in
Listing 7.6, you need to replace the android-apt plugin for annotation processing with
Kotlin’s kapt plugin.
Setting Up Kotlin for Android 209

Listing 7.6 Configuring kapt for Annotation Processing (Example: Dagger 2)


apply plugin: 'kotlin-kapt' // Enables kapt for annotation processing
// …

dependencies {
implementation 'com.google.dagger:dagger:2.17'
kapt 'com.google.dagger:dagger-compiler:2.17' // Uses kapt
}

The first step is to enable the plugin kotlin-kapt, which replaces android-apt for annotation
processing—it will handle annotations on Java files as well, so any dependencies on apt can be
removed. After that, dependencies can be added to the kapt configuration, such as the dagger-
compiler in Listing 7.6, while normal dependencies are still added as usual.

To process annotations in test and androidTest sources, you can use the more specialized kapt-
Test and kaptAndroidTest configurations to add the required dependencies. Any dependencies
from the kapt configuration are inherited to these two, however, so there is no need to include
them twice—dependencies added using kapt are available in both test and production code.

Converting Java Code to Kotlin


If you’re not starting with a fresh Kotlin project but want to mix Kotlin into an existing Java
project, you can do so by first invoking the Configure Kotlin in Project action as above. After that,
you can make use of the Java-to-Kotlin converter to transpile an existing Java file to a Kotlin file.
To do this, you can click Convert Java File to Kotlin File under the Code menu. Alternately, you can
use Ctrl+Shift+A (or Cmd+Shift+A on Mac) and type “cjtk” (for Convert Java to Kotlin) to fuzzy-
search for that same action and invoke it.

Be aware that you may want to keep a copy of the original Java file, especially if you’re doing this
with a production app. Ideally, I recommend you start introducing Kotlin into an existing app by
converting non-business-critical parts of your app, or better yet, start by migrating a pet project.
Chapter 10, Migrating to Kotlin, covers processes and best practices to migrate Java apps to Kotlin.

Note
The quality of autoconverted Kotlin code may not be production-ready. For instance, it won’t
make use of Kotlin’s powerful language features where appropriate. So, although the converter
is good for getting used to the basic syntax and features of Kotlin, it will not open up all the
possibilities that the language offers you.

In short, Android Studio 3 has strong support for Kotlin and avoids the need for any manual
configuration for basic Kotlin projects. You can use Kotlin out of the box and integrate it into
existing Java apps. In all this, Android Studio supports your workflow with actions you can easily
invoke that, for instance, adjust the Gradle build or convert Java files to Kotlin.
210 Chapter 7 Android App Development with Kotlin: Kudoo App

App #1: Kudoo, a To-Do List App


It’s time to write your first app with Kotlin: Kudoo. This simple to-do list app lets users create
to-do items and check them off when they’re completed. Although this app is basic, it already
introduces several fundamental Android components and Kotlin language features. Most impor-
tant are the following:
 Android Architecture Components to split the app into loosely coupled layers
 Recycler Views to improve the list view performance
 Kotlin Android Extensions to omit findViewById
 Intents to switch between activities and share data
 Coroutines for database operations

Figure 7.2 shows the finished app.

Figure 7.2 The finished to-do app you will create in this chapter

Let’s start by creating a new project with a basic activity.

Creating the Project


Under the File menu, select New and New Project. Name the app Kudoo, as shown in Figure 7.3,
and use any unique company domain; example.com will do if you don’t have one as you won’t
be publishing this app to the Play Store. Finally, select Include Kotlin support.
App #1: Kudoo, a To-Do List App 211

Note
In case the following screenshots do not exactly match the most current version of Android
Studio that you’re using, please follow the up-to-date instructions from the Android Developers
website2 to create a project with a Basic Activity and Kotlin support.

Figure 7.3 Create Android Project screen

After clicking Next, you can keep Phone & Tablet selected with the default minimum SDK (API 19 at
the time of writing). Click Next again and select Basic Activity (not Empty Activity) as the template.
Click Next, and keep the defaults in order to generate a MainActivity.kt file along with its
layout file (see Figure 7.4). Click Finish, and Android Studio will set up and build your project.

Figure 7.4 Create a MainActivity with its layout

2. https://developer.android.com/studio/projects/create-project
212 Chapter 7 Android App Development with Kotlin: Kudoo App

Once it’s done, make sure that the module’s build.gradle file is set up correctly. You can easily
navigate to it using Ctrl+Shift+N (Cmd+Shift+O on Mac) and typing in “build.gradle,” then
selecting the one from the app module, or by using the Android project view as was shown in
Figure 7.1. You should see the following two lines in there:

apply plugin: 'kotlin-android'


apply plugin: 'kotlin-android-extensions'

You now have a barebones app that should run fine when you launch it (using the green arrow
button or Shift+F10/Ctrl+R on Mac). This is a good time to try running it to make sure everything’s
working as expected and to set up an Android Virtual Device (AVD)3 if you don’t have one yet.

To finish up the project template, you can remove the menu because you won’t need that
for this simple app. To do that, remove the methods onCreateOptionsMenu and onOptionsItem
Selected from MainActivity.kt and remove the resources folder res/menu. With this, you’re
all set to start writing the app.

Note
If you want to use Git while working on this app, you can visit gitignore.io to generate the con-
tent for the .gitignore file. I recommend using one configured for Android, Android Studio,
Kotlin, and Java.4
Android Studio creates a .gitignore file automatically, you can use Ctrl+Shift+N (Cmd+
Shift+O on Mac) and type in “.gitignore” to find it (it doesn’t show up in the Android project view),
then choose the one in the root directory, the one with the (Kudoo) suffix. Replace all its con-
tents with the ones from gitignore.io. To run git init, the integrated Terminal comes in handy;
you can open that using Alt+F12 from inside Android Studio.

The code for the Kudoo app is on GitHub5 and the repository has a directory for each working
state of the app, corresponding to the results after each section in this book. So if you get an error,
you can always compare to the directory corresponding to the section you are reading. Definitely
do write the apps yourself while reading this chapter and Chapter 8; I guarantee you will learn a
lot more this way.

Adding the Recycler View


The app’s central element will be the list of to-do items, implemented using a RecyclerView.
A RecyclerView is a list view that avoids lag even for very long lists by reusing the view objects
required to display the list. A good way to start out with this app is to implement the basic
RecyclerView and feed it some sample data to show. Here’s an overview of the involved steps to
achieve this (which are covered in this section).

3. Set up a virtual device: https://developer.android.com/studio/run/managing-avds


4. https://www.gitignore.io/api/java%2Ckotlin%2Candroid%2Candroidstudio
5. https://github.com/petersommerhoff/kudoo-app
App #1: Kudoo, a To-Do List App 213

1. First, adjust and create all the required layouts in XML.

2. Create a simple to-do item class that represents the data shown in each list item.

3. Implement the RecyclerView adapter that feeds the recycler view with data.

4. Set up the RecyclerView in the MainActivity.

Setting up the Recycler View Layouts


Because you chose Basic Activity during project setup, there is an activity_main.xml layout file
inside the res/layout directory. This layout includes the content_main.xml layout. Inside the
content_main.xml, replace the TextView with a RecyclerView so that the layout file looks as
it does in Listing 7.7.

Listing 7.7 RecyclerView Layout


<android.support.constraint.ConstraintLayout
app:layout_behavior="@string/appbar_scrolling_view_behavior" …>

<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerViewTodos"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.constraint.ConstraintLayout>

With this setup, the RecyclerView takes up all the height and width of its container so that it
effectively fills the whole screen. Note that the ConstraintLayout that was generated for you
enables scrolling if the list overflows the screen via its layout_behavior attribute.

Note
If the RecyclerView cannot be found, make sure you have the Design Support Library in your
module’s dependencies:
implementation 'com.android.support:design:27.1.1'

Next, let’s create the layout that each list item in the RecyclerView will use. This comprises
a TextView to show the to-do list title with a CheckBox beside it to check it off. Under res/
layout, add a new layout resource file called todo_item.xml. A simple LinearLayout as in
Listing 7.8 will do; add it to the newly created layout file.
214 Chapter 7 Android App Development with Kotlin: Kudoo App

Listing 7.8 RecyclerView Item Layout


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<CheckBox
android:id="@+id/cbTodoDone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_medium" />

<TextView
android:id="@+id/tvTodoTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/padding_large"
android:textSize="@dimen/font_large" />

</LinearLayout>

To add the missing dimension resources (the ones starting with @dimen), press Alt+Enter with the
cursor inside them and click the suggested action Create dimen value resource. The margin_medium
and padding_large should be 16dp, font_large should be 22sp. Alternately, you can add
these resources to res/values/dimens.xml6 by hand.

Note
Android uses several types of resources. Dimensions are one of them; others include strings,
layouts, and drawables. All reside in the res directory, and simple values like dimensions and
strings are inside the res/values subdirectory. Looking at the files inside res/values, you
will see they all share the same basic structure. You can add resources into these files manu-
ally as well, instead of using Android Studio’s actions to create them for you.
All resources are accessed programmatically via the generated R class, such as R.string.
enter_todo or R.layout.activity_main.

That’s all the required layout for now, so you can now dive into the actual Kotlin code, beginning
with the model.

6. https://github.com/petersommerhoff/kudoo-app/blob/master/01_AddingRecyclerView/app/src/
main/res/values/dimens.xml
App #1: Kudoo, a To-Do List App 215

Model
Models represent the entities used in your app. The only model this app needs is one to represent
a to-do item, and Kotlin’s data classes greatly simplify its declaration, as Listing 7.9 shows. Place
this class into a new model package directly under the kudoo package.

Listing 7.9 TodoItem Data Class


data class TodoItem(val title: String)

This simple to-do item model that carries only a title is all you need as models for this app.

The RecyclerView Adapter


The main work when using RecyclerView is implementing the adapter. The adapter provides
the data for the RecyclerView (the list items) and handles how the item views are reused. This
is how the RecyclerView improves performance: It reuses existing views from a so-called view
holder to avoid creating a new object and more importantly to avoid inflating the layouts for these
views. In contrast, a normal ListView would create and inflate dedicated views for every list
item, without any reuse.

The first step is to add a new package view with subpackage main (you can do this in one step in
Android Studio by typing in view.main as the package name in the package creation dialog) and
to add a new Kotlin class RecyclerListAdapter inside it, as in Listing 7.10. In total, the package
should now be com.example.kudoo.view.main, assuming you used example.com as your
company domain during project creation.

Listing 7.10 RecyclerListAdapter Signature


import android.support.v7.widget.RecyclerView
import com.example.kudoo.model.TodoItem

class RecyclerListAdapter(
private val items: MutableList<TodoItem>
) : RecyclerView.Adapter<RecyclerListAdapter.ViewHolder>() { // ViewHolder impl. next
// …
}

Because the RecyclerView will show to-do items and users may add or remove items on the list,
the adapter carries a MutableList<TodoItem>, which represents the data that will be shown in
the RecyclerView.
216 Chapter 7 Android App Development with Kotlin: Kudoo App

This class extends Android’s RecyclerView.Adapter and thus has to override three methods:

1. onCreateViewHolder: This method creates a ViewHolder object, meaning an object


that is used to hold all the views for a single list item. In this case, one TextView and one
CheckBox. As mentioned, the point is that the recycler view then reuses these view objects
to avoid unnecessary object creations and expensive layout inflations.

2. onBindViewHolder: This method binds a given TodoItem to such a ViewHolder,


meaning that it populates the views with the data from the TodoItem. Here, it shows
the to-do title in the text view.

3. getItemCount: This must return the number of items to be shown in the list.

It’s common practice to add the custom ViewHolder class that implements RecyclerView.
ViewHolder as a nested class into the adapter. This view holder class holds all views for a
single list item (the text view and the check box) and knows how to bind a TodoItem to
them. Listing 7.11 shows the ViewHolder for this app.

Listing 7.11 Custom ViewHolder


import android.support.v7.widget.RecyclerView
import android.view.View
import android.widget.*
import com.example.kudoo.R
import com.example.kudoo.model.TodoItem

class RecyclerListAdapter(…) : … {
// …
class ViewHolder(listItemView: View) : RecyclerView.ViewHolder(listItemView) {
// ViewHolder stores all views it needs (only calls ‘findViewById’ once)
val tvTodoTitle: TextView = listItemView.findViewById(R.id.tvTodoTitle)

fun bindItem(todoItem: TodoItem) { // Binds a to-do item to the views


tvTodoTitle.text = todoItem.title // Populates the text view with the to-do
cbTodoDone.isChecked = false // To-do items are always 'not done' (or deleted)
}
}
}

As you can see, the ViewHolder caches all its views and only calls findViewById once when
initialized. This is part of how a RecyclerView improves performance compared to the old
ListView: it reuses existing view objects and populates them with the desired data in bindItem,
without doing any expensive operations.
App #1: Kudoo, a To-Do List App 217

You must use findViewById here because the ViewHolder is not a LayoutContainer. If you
want, you can change this using the Kotlin Android Extensions by enabling experimental features
(where experimental does not mean unstable). To do so, simply add the code from Listing 7.12 to
the module’s build.gradle file and sync the project. You can place it below the dependencies
{ … } section.

Listing 7.12 Enabling Experimental Kotlin Android Extensions


androidExtensions {
experimental = true
}

With this, you can now get rid of findViewById in the ViewHolder by implementing the
LayoutContainer interface that’s now available, as shown in Listing 7.13. All that’s required to
implement this interface is to override the containerView property, which is what was called
listItemView in Listing 7.11. Thus, you can override it directly in the constructor parameter.
Then, you can access the UI elements directly by the ID you gave them in the XML layout file,
here tvTodoTitle and dbTodoDone. Being able to access UI elements like this, without explic-
itly calling findViewById, is one of the popular benefits of Kotlin on Android and is enabled by
the Kotlin Android Extensions.

Listing 7.13 ViewHolder Without findViewById


// … (imports from before here)
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.todo_item.* // Note synthetic import

class RecyclerListAdapter(…) : … {
// …
class ViewHolder(
override val containerView: View // Overrides property from LayoutContainer
) : RecyclerView.ViewHolder(containerView), LayoutContainer {

fun bindItem(todoItem: TodoItem) {


tvTodoTitle.text = todoItem.title // Still calls findViewById only once
cbTodoDone.isChecked = false
}
}
}

Note that the LayoutContainer caches all views as well, although it is not directly apparent.
You can see that this is the case by looking at the decompiled Java code—remember, you can use
Ctrl+Shift+A (Cmd+Shift+A), then type “Show Kotlin Bytecode” or simply “skb” and then click
218 Chapter 7 Android App Development with Kotlin: Kudoo App

Decompile. Alternately, you can use the Tools menu; under Kotlin there’s the option Show Kotlin
Bytecode.

With this, all that’s left to do is to override the three methods mentioned above in the adapter.
First, onCreateViewHolder must create a ViewHolder object and should use the list item layout
(todo_item.xml) you’ve created to inflate the view’s layout. This is commonly done using a
LayoutInflater, as shown in Listing 7.14.

Listing 7.14 RecyclerListAdapter.onCreateViewHolder()


// … (imports from before)
import android.view.LayoutInflater
import android.view.ViewGroup

class RecyclerListAdapter(…) : … {
// …
override fun onCreateViewHolder(parent: ViewGroup, layoutId: Int): ViewHolder {
val itemView: View = LayoutInflater.from(parent.context)
.inflate(R.layout.todo_item, parent, false) // Creates a list item view
return ViewHolder(itemView) // Creates a view holder for it
}
}

This first creates a new view (a list item) by inflating the list item layout. It does so without attach-
ing the view to any parent yet because the third argument of inflate is set to false. Then it
passes that to a new ViewHolder that manages this view from then on and will let the recycler
view reuse it later.

Next up is onBindViewHolder, which should bind a given TodoItem to a ViewHolder. The


logic for this is already implemented in ViewHolder.bindItem so that you can delegate to
that method, as done in Listing 7.15. Place this into the RecyclerListAdapter class, like
onCreateViewHolder.

Listing 7.15 RecyclerListAdapter.onBindViewHolder()


override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bindItem(items[position]) // Populates the list item with to-do data
}

Lastly, getItemCount is the easiest to implement since the RecyclerView should render as
many items as the list of to-do items given to its adapter. Listing 7.16 implements this method.
This also goes into the RecyclerListAdapter class.
App #1: Kudoo, a To-Do List App 219

Listing 7.16 RecyclerListAdapter.getItemCount()


override fun getItemCount() = items.size

Putting everything together, the RecyclerListAdapter looks as it does in Listing 7.17.

Listing 7.17 Complete RecyclerListAdapter


import android.support.v7.widget.RecyclerView
import android.view.*
import com.example.kudoo.R
import com.example.kudoo.model.TodoItem
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.todo_item.*

class RecyclerListAdapter(
private val items: MutableList<TodoItem>
) : RecyclerView.Adapter<RecyclerListAdapter.ViewHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, layoutId: Int): ViewHolder {


val itemView: View = LayoutInflater.from(parent.context)
.inflate(R.layout.todo_item, parent, false)
return ViewHolder(itemView)
}

override fun getItemCount() = items.size

override fun onBindViewHolder(holder: ViewHolder, position: Int) {


holder.bindItem(items[position])
}

class ViewHolder(
override val containerView: View
) : RecyclerView.ViewHolder(containerView), LayoutContainer {

fun bindItem(todoItem: TodoItem) {


tvTodoTitle.text = todoItem.title
cbTodoDone.isChecked = false
}
}
}
220 Chapter 7 Android App Development with Kotlin: Kudoo App

The MainActivity
With the RecyclerView ready to go, now you only need to set it up in the MainActivity and
populate it with some sample data. Thanks to Kotlin Android Extensions, the RecyclerView can
be accessed directly by its layout ID, recyclerViewTodos, so that you can again avoid find-
ViewById. Listing 7.18 shows the setup logic.

Listing 7.18 Setting Up the RecyclerView with the Adapter


// … (imports from before)
import android.support.v7.widget.*
import com.example.kudoo.model.TodoItem
import com.example.kudoo.view.main.RecyclerListAdapter
import kotlinx.android.synthetic.main.activity_main.* // From Kotlin Android Ext.
import kotlinx.android.synthetic.main.content_main.* // From Kotlin Android Ext.

class MainActivity : AppCompatActivity() {


// …
private fun setUpRecyclerView() = with(recyclerViewTodos) {
adapter = RecyclerListAdapter(sampleData()) // Populates adapter/list with data
layoutManager = LinearLayoutManager(this@MainActivity) // Uses linear layout
itemAnimator = DefaultItemAnimator() // Optional layout niceties
addItemDecoration(
DividerItemDecoration(this@MainActivity, DividerItemDecoration.VERTICAL))
}

private fun sampleData() = mutableListOf(


TodoItem("Implement RecyclerView"),
TodoItem("Store to-dos in database"),
TodoItem("Delete to-dos on click")
)
}

In setUpRecyclerView, the adapter is assigned to an instance of your RecyclerListAdapter,


with sample data passed in as the list of to-do items that should be displayed. Next, a simple
LinearLayoutManager is used to lay out the items as a vertical list. As an optional bonus, an
item animator is added to improve the delete animation and a divider item decorator adds a sepa-
rator line between each item. Note how this code makes use of the with function and shorthand
function syntax.
App #1: Kudoo, a To-Do List App 221

Note
Beware not to import R.id.recyclerViewTodos instead of the synthetic property even when
it cannot be found. If Android Studio marks these references in red, rebuild the project using
Ctrl+F9 (Cmd+F9 on Mac) or by running the app.
In autocompletions, Android Studio marks the correct imports with an (Android Extensions)
suffix. They’re from packages like kotlinx.android.synthetic.main.content_main.*.

Now, you just need to call setUpRecyclerView in onCreate and you should see the three
sample to-do items displayed in your app. This is shown in Listing 7.19.

Listing 7.19 Adjusting onCreate


override fun onCreate(savedInstanceState: Bundle?) {
// …
setUpRecyclerView()
}

The most central component is now ready, but the data is hard-coded in the MainActivity. In
the next step, you’ll use Room to retrieve sample data from a SQLite database instead.

Adding a Room Database


With this section, you’ll start integrating Android Architecture Components into this app.
Introduced at Google I/O 2017, this library of architecture components has rapidly gained wide-
spread use. Room is a component that handles database access and greatly simplifies the use of
SQLite. The ViewModel and LiveData architecture components will be integrated afterward.

To be able to use Room, you must add the dependencies from Listing 7.20 to your module’s
build.gradle file (you can also extract dependency versions into the project’s build.gradle
file if you prefer).

Listing 7.20 Gradle Dependencies for Room


dependencies {
// …
def room_version = "1.1.1" // Use latest version 1.x if you want
implementation "android.arch.persistence.room:runtime:$room_version"
kapt "android.arch.persistence.room:compiler:$room_version"
}

Remember to use kapt instead of annotationProcessor when using Kotlin, and add the corre-
sponding plugin at the top of the build.gradle file, as in Listing 7.21.
222 Chapter 7 Android App Development with Kotlin: Kudoo App

Listing 7.21 Enabling the Kotlin Annotation Processor Plugin


apply plugin: 'kotlin-android-extensions' // Should already exist
apply plugin: 'kotlin-kapt' // Added now for annotation processing

Room makes it easy to store models to a database, but first you’ll have to tell Room which models
to store. For this app, only the TodoItem class should be mapped to the database. To let Room
know, you must annotate it with @Entity. In addition, a @PrimaryKey is required to uniquely
identify each to-do item in the database. Listing 7.22 shows the adjusted model.

Listing 7.22 TodoItem as an Entity


import android.arch.persistence.room.Entity
import android.arch.persistence.room.PrimaryKey

@Entity(tableName = "todos") // Indicates that this is a database entity


data class TodoItem(val title: String) {
@PrimaryKey(autoGenerate = true) // Unique primary key must identify an object
var id: Long = 0 // 0 is considered 'not set' by Room
}

Inside @Entity, you can specify a custom name for the associated database table; here, it will
be todos. An additional id serves as the primary key. Room will autogenerate these IDs for you
when setting autoGenerate = true; it does so by simply incrementing it by one for each new
record. Note that the id is initialized to zero because Room will consider zero as being not set,
thus allowing it to set it to the autogenerated value.

With only these few lines of code, Room has all the information it needs to map TodoItem
objects to a database table. What’s next is to access that table using a data access object (DAO)—
this is your access point for all database operations and will be generated by Room as well. All you
have to do is define an interface with the operations and queries you want to use. This is shown in
Listing 7.23. You can place this TodoItemDao class in a new db package, directly under the kudoo
package.

Listing 7.23 TodoItemDao for Database Access


import android.arch.persistence.room.*
import android.arch.persistence.room.OnConflictStrategy.IGNORE
import com.example.kudoo.model.TodoItem

@Dao
interface TodoItemDao {
App #1: Kudoo, a To-Do List App 223

@Query("SELECT * FROM todos")


fun loadAllTodos(): List<TodoItem> // Allows retrieving all to-do items

@Insert(onConflict = IGNORE) // Does nothing if entry with ID already exists


fun insertTodo(todo: TodoItem) // Allows inserting a new to-do item

@Delete
fun deleteTodo(todo: TodoItem) // Allows deleting an existing to-do item
}

By annotating the interface with @Dao, you let Room know to generate its implementation. Inside
the DAO, you can use @Query, @Insert, @Update, and @Delete. The latter three need no further
setup. For @Insert and @Update, you may set a strategy for the on-conflict case, which defines
how Room behaves if an element with the same ID already exists. @Query allows you to imple-
ment arbitrary queries on your database. The only query you need here is one to load all to-do
items. Room validates your queries at compile-time and Android Studio analyzes them instantly
as well, giving you a very fast feedback loop.

You’re almost done implementing the database. The last step is to implement a RoomDatabase.
This is done using an abstract class that extends RoomDatabase and is annotated with
@Database. Also, it should provide an instance of itself to the outside. In Listing 7.24,
AppDatabase fills that role. This also belongs into the db package.

Listing 7.24 AppDatabase


import android.arch.persistence.room.*
import android.content.Context // Needs access to Android context to build DB object
import com.example.kudoo.model.TodoItem

@Database(entities = [TodoItem::class], version = 1) // TodoItem is only DB entity


abstract class AppDatabase : RoomDatabase() {

companion object {
private var INSTANCE: AppDatabase? = null

fun getDatabase(ctx: Context): AppDatabase { // Builds and caches DB object


if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(ctx, AppDatabase::class.java, "AppDatabase")
.build()
}
224 Chapter 7 Android App Development with Kotlin: Kudoo App

return INSTANCE!!
}
}

abstract fun todoItemDao(): TodoItemDao // Triggers Room to provide an impl.


}

The @Database annotation requires all entities the database should contain as well as a version
number. Whenever the schema changes, you must increase this version number.

Recall that companion object members work like static members in languages like Java. The
AppDatabase caches an instance of itself in a private INSTANCE property that is initialized lazily
when first accessed. This initialization uses Room’s database builder to build an implementation
of the abstract AppDatabase. Lastly, add abstract methods for any DAOs you want to expose for
accessing this database—here, you only need the TodoItemDao.

With this, the database will be set up correctly and is in principle ready to be used in the
MainActivity. However, there’s no sample data in it. The clean way to prepopulate a Room data-
base is to add a callback when instantiating it. Room’s callbacks allow you to override an onCre-
ate method, which is exactly what you need to add sample data when the database is created.
Listing 7.25 shows the adjusted code for the companion object. You will only need this code
temporarily until users can create their own to-do items.

Listing 7.25 Populating the AppDatabase with Sample Data


// … (imports from before)
import android.arch.persistence.db.SupportSQLiteDatabase
import kotlinx.coroutines.experimental.*

val DB = newSingleThreadContext("DB") // CoroutineContext for DB operations

@Database(entities = [TodoItem::class], version = 1)


abstract class AppDatabase : RoomDatabase() {

companion object {
private var INSTANCE: AppDatabase? = null

fun getDatabase(ctx: Context): AppDatabase {


if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(ctx, AppDatabase::class.java, "AppDatabase")
App #1: Kudoo, a To-Do List App 225

.addCallback(prepopulateCallback(ctx)) // Adds callback to database


.build()
}

return INSTANCE!!
}

private fun prepopulateCallback(ctx: Context): Callback {


return object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) { // Uses onCreate callback
super.onCreate(db)
populateWithSampleData(ctx)
}
}
}

private fun populateWithSampleData(ctx: Context) { // Adds sample data to DB


launch(DB) { // DB operations must be done on a background thread
with(getDatabase(ctx).todoItemDao()) { // Uses DAO to insert items into DB
insertTodo(TodoItem("Create entity"))
insertTodo(TodoItem("Add a DAO for data access"))
insertTodo(TodoItem("Inherit from RoomDatabase"))
}
}
}
}

abstract fun todoItemDao(): TodoItemDao


}

Here, you extend Room’s Callback to override its onCreate method and insert the sample data.
Inserting data is a database operation and therefore must be performed on a background thread.
As you can see, the code uses launch { … } to perform the database operations in the back-
ground. It uses a dedicated single-thread context for database operations because CommonPool is
intended for CPU-bound operations. To make this code work, you need to include the coroutine
dependencies in your module’s build.gradle file, as in Listing 7.26.
226 Chapter 7 Android App Development with Kotlin: Kudoo App

Listing 7.26 Gradle Dependencies for Kotlin Coroutines


def coroutines_version = "0.24.0" // Use latest version if you want
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"

With this, the database is finished and will even be populated with sample data when first
created. All that’s left to do is use it in MainActivity, as shown in Listing 7.27.

Listing 7.27 Using the Database from MainActivity


// … (imports from before)
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.*
import com.example.kudoo.db.*

class MainActivity : AppCompatActivity() {

private lateinit var db: AppDatabase // Stores an AppDatabase object

override fun onCreate(savedInstanceState: Bundle?) {


// …
db = AppDatabase.getDatabase(applicationContext)
setUpRecyclerView() // Sets up recycler view *after* db reference is initialized
// …
}

private fun setUpRecyclerView() = with(recyclerViewTodos) {


launch {
val todos = sampleData().toMutableList()
withContext(UI) { adapter = RecyclerListAdapter(todos) } // Uses UI context
}
layoutManager = LinearLayoutManager(this@MainActivity)
itemAnimator = DefaultItemAnimator()
addItemDecoration(
DividerItemDecoration(this@MainActivity, DividerItemDecoration.VERTICAL))
}

private suspend fun sampleData() =


withContext(DB) { db.todoItemDao().loadAllTodos() } // Uses DB context
}
App #1: Kudoo, a To-Do List App 227

For now, the MainActivity holds a reference to the AppDatabase to access the database via the
DAO. This reference can only be initialized inside onCreate once the application context is avail-
able, so it uses a late-initialized property.

As a database operation, the actual call to loadAllTodos must be performed in the background.
For this, it uses withContext(DB) { … } to run it on the dedicated database dispatcher and
retrieve a result. Due to the withContext call, sampleData must be a suspend function, so its
call is wrapped inside a launch { … } in setUpRecyclerView.

That’s it! You’ve now set up a simple Room database with Kotlin, written sample data into the
database, and retrieved it back to show in the UI. You can run this app now to see the sample data
from the database.

Troubleshooting
If something went wrong trying to prepopulate the database the first time, you can delete it in
order to trigger onCreate again. To do so, use Android Studio’s Device File Explorer to remove
the directory data/data/com.example.kudoo/databases.
Also, at the time of writing, Apply Changes can cause issues in combination with coroutines. If
you get an error mentioning “CoroutineImpl.label is inaccessible,” try re-running the
app normally without Apply Changes.

As the next step, you will introduce a ViewModel to avoid the direct dependency on
AppDatabase in MainActivity.

Using a ViewModel
A view model is an Android Architecture Component that holds the data for an associated activ-
ity. There are several benefits to this approach:
 Activities only need to know of their view model(s) to get all the data they need, unaware
of whether that data comes from a cache, a database, a network call, or another data source.
In other words, it decouples the activity from the data source.
 Android’s ViewModel is lifecycle-aware, meaning that it automatically preserves data across
configuration changes such as screen rotations. This way, data doesn’t need to be reloaded
after each configuration change.
 Activities should not perform asynchronous calls themselves because these may potentially
take a long time and the activity has to manage them to avoid memory leaks. Separating
this concern into its own class is therefore cleaner and avoids huge activity classes that try
to perform all app logic themselves.

Let us reap these benefits by using Android’s ViewModel class in the Kudoo app. The first step is
to add the required dependencies shown in Listing 7.28. These already include LiveData as well,
which you’ll incorporate in the next step.
228 Chapter 7 Android App Development with Kotlin: Kudoo App

Listing 7.28 Gradle Dependencies for ViewModel (and LiveData)


dependencies {
// …
def lifecycle_version = "1.1.1" // Replace with latest version if you want
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
kapt "android.arch.lifecycle:compiler:$lifecycle_version"
}

Next, add a new package viewmodel (under kudoo) and add a new TodoViewModel class, which
will be the view model for MainActivity. A view model should extend either the ViewModel
or the AndroidViewModel class—the latter is required if the ViewModel requires an application
context. Thus, the TodoViewModel will extend AndroidViewModel to be able to construct an
AppDatabase because the database needs the application context. So you get the class header
shown in Listing 7.29.

Listing 7.29 Class Header of TodoViewModel


import android.app.Application
import android.arch.lifecycle.AndroidViewModel

class TodoViewModel(app: Application) : AndroidViewModel(app) { … }

Every subclass of AndroidViewModel must accept an Application object in its constructor and
pass it along to its parent. This is how the view model becomes aware of the application context.
This view model wraps the database and provides a clean API for the MainActivity to use.
Listing 7.30 introduces the required members.

Listing 7.30 Complete TodoViewModel


// … (imports from before)
import com.example.kudoo.db.*
import com.example.kudoo.model.TodoItem
import kotlinx.coroutines.experimental.*

class TodoViewModel(app: Application) : AndroidViewModel(app) {


private val dao by lazy { AppDatabase.getDatabase(getApplication()).todoItemDao() }

suspend fun getTodos(): MutableList<TodoItem> = withContext(DB) {


dao.loadAllTodos().toMutableList()
}
fun add(todo: TodoItem) = launch(DB) { dao.insertTodo(todo) }
fun delete(todo: TodoItem) = launch(DB) { dao.deleteTodo(todo) }
}
App #1: Kudoo, a To-Do List App 229

This view model lazily requests a database instance when it’s accessed for the first time. It provides
a suspending function to retrieve all to-do items from the database. Additionally, it exposes
methods to add and delete to-do items, which launch the corresponding database operations in
the background.

With this, you can now replace the AppDatabase in MainActivity with the new TodoViewModel.
To this end, remove the AppDatabase property and add the TodoViewModel instead, as shown in
Listing 7.31.

Listing 7.31 Integrating the TodoViewModel into MainActivity


class MainActivity : AppCompatActivity() {

private lateinit var viewModel: TodoViewModel // Now references view model, not DB

override fun onCreate(savedInstanceState: Bundle?) {


// …
viewModel = getViewModel(TodoViewModel::class) // ‘getViewModel’ is impl. next
setUpRecyclerView()
}

private fun setUpRecyclerView() = with(recyclerViewTodos) {


launch(UI) { adapter = RecyclerListAdapter(viewModel.getTodos()) }
// …
}
}

The view model is again late-initialized because it requires the activity to be attached to the
application—and this is the case in onCreate. You can remove the sampleData method and
simply pass in viewModel.getTodos() to the RecyclerView’s adapter. Because this performs
a database operation, it is wrapped inside launch. In this regard, the MainActivity is not yet
completely independent of the implementation details of the ViewModel—but we’ll fix this
using LiveData in the next step.

For now, you have to add the getViewModel extension function to make this code compile.
Remember that extensions are your best friend on Android to work around API boilerplate. Here,
you use it to retrieve view models more easily. Create a new package view.common and add a new
file ViewExtensions.kt to it. Inside this file, you can define the extension shown in Listing 7.32
as a file-level function.
230 Chapter 7 Android App Development with Kotlin: Kudoo App

Listing 7.32 Extension to Retrieve View Models


import android.arch.lifecycle.*
import android.support.v4.app.FragmentActivity
import kotlin.reflect.KClass

fun <T : ViewModel> FragmentActivity.getViewModel(modelClass: KClass<T>): T =


ViewModelProviders.of(this).get(modelClass.java)

It’s an extension on the FragmentActivity class that simply accepts a KClass<T>


(a Kotlin class) where T must be a ViewModel. In this way, it provides a more natural API
to retrieve your view models. You must import it into your MainActivity to resolve the
remaining error.

Tip
Kotlin’s extension functions are extraordinarily useful to avoid repeating boilerplate
around Android APIs. For a collection of extensions that help you write concise
and expressive code, and that is maintained by Google themselves, check out
Android KTX.7
At the time of writing, Android KTX is still in alpha and APIs are prone to change.
So to make sure you can follow along these apps seamlessly, they are not used here.
When you’re reading this, Android KTX may be stable, and if so, I recommend exploring
the extensions.

You have now integrated Android’s ViewModel into your app, preserving your
data across configuration changes and helping to separate concerns between activities
and data-handling code—the activity should only be responsible for showing the data
and providing notifications of user actions. However, so far the to-do items retrieved from
the view model don’t automatically reflect changes to the data. You can verify this by
calling viewModel.add(…) after some delay—the new item will not yet show up in the
UI. To handle this idiomatically with Android Architecture Components, let’s integrate
LiveData next.

Integrating LiveData
LiveData is a lifecycle-aware data holder. App components like activities and fragments can
observe a LiveData object to automatically reflect data changes in the UI. Because it’s lifecycle
aware, LiveData makes sure to notify only active observers. For instance, it doesn’t update
activities that are currently in the background or that have been destroyed by Android to recover
memory. Like ViewModel, this has several benefits.

7. https://developer.android.com/kotlin/ktx
App #1: Kudoo, a To-Do List App 231

 Activities don’t have to handle lifecycles, they can simply observe a LiveData, which
makes sure not to send data to inactive consumers (which would crash the app).
 Data is automatically kept up to date whenever the activity is active. For instance, after
configuration change, the activity will immediately receive the latest data.
 LiveData makes all its observers perform cleanups when their associated lifecycle is
destroyed so that memory leaks are prevented.

Integrating LiveData into the app is fairly simple because it works well with Room and
ViewModel out of the box. As a first step, the DAO should return a LiveData instead of just a
List<TodoItem> so that it can be observed for data changes. Fortunately, Room can do this for
you automatically; just wrap the return value into LiveData as in Listing 7.33 and Room will
perform the required transformation.

Listing 7.33 Returning LiveData from the DAO


// … (imports from before)
import android.arch.lifecycle.LiveData

@Dao
interface TodoItemDao {
// …
@Query("SELECT * FROM todos")
fun loadAllTodos(): LiveData<List<TodoItem>> // Wraps return type in LiveData now
}

Next, you’ll have to adjust TodoViewModel.getTodos accordingly, as done in Listing 7.34.

Listing 7.34 Returning LiveData from the ViewModel


// … (imports from before)
import android.arch.lifecycle.LiveData

class TodoViewModel(app: Application) : AndroidViewModel(app) {


// Now uses a LiveData of a read-only list
suspend fun getTodos(): LiveData<List<TodoItem>> = withContext(DB) {
dao.loadAllTodos()
}
// …
}
232 Chapter 7 Android App Development with Kotlin: Kudoo App

Now you’re ready to observe the LiveData in MainActivity, as shown in Listing 7.35.

Listing 7.35 Observing the LiveData from MainActivity


// … (imports from before)
import android.arch.lifecycle.LiveData
import kotlinx.coroutines.experimental.android.UI

class MainActivity : AppCompatActivity() {


// …
private fun setUpRecyclerView() { // No longer uses shorthand notation
with(recyclerViewTodos) {
adapter = RecyclerListAdapter(mutableListOf()) // Initializes with empty list
// …
}

launch(UI) { // Uses UI thread to access recycler view adapter


val todosLiveData = viewModel.getTodos() // Runs in DB context
todosLiveData.observe(this@MainActivity, Observer { todos ->
// Observes changes in the LiveData
todos?.let {
val adapter = (recyclerViewTodos.adapter as RecyclerListAdapter)
adapter.setItems(it) // Updates list items when data changes
}
})
}
}
}

Here, the RecyclerView adapter is first initialized with an empty list. Note that you no
longer need launch to assign the adapter. To add data to the list, the view model’s
LiveData is observed for changes—on any change, the adapter will show the new list
of to-do items. In effect, when a new record is inserted into or removed from the database,
Room automatically reflects this in the LiveData, which in turn notifies its observers
(the MainActivity) of the data change, finally causing the RecyclerView to
update.

To make this code compile, you must add the setItems method to the RecyclerListAdapter
class, as shown in Listing 7.36.
App #1: Kudoo, a To-Do List App 233

Listing 7.36 Adding setItems to the RecyclerListAdapter


class RecyclerListAdapter(
private val items: MutableList<TodoItem>
) : RecyclerView.Adapter<RecyclerListAdapter.ViewHolder>() {
// …
fun setItems(items: List<TodoItem>) {
this.items.clear()
this.items.addAll(items)
notifyDataSetChanged() // Must notify recycler view of changes to the data
}
}

After updating the list of items the RecyclerView should display, remember to call notify-
DataSetChanged to trigger redrawing the view. For large lists, you would want to use DiffUtil8
to improve performance, but it’s not necessary for this simple to-do list.

This is all that’s required to react to data changes using LiveData and immediately update your
UI to show the latest data. You can verify this works by calling viewModel.add(…) in your
MainActivity to add a new item (you may want to use delay as well to see it appear). Or you
can wait until you complete the next step that allows users to add new to-do items.

Adding New To-Do Items


All infrastructure for data persistence and data presentation in the UI is set up now. So what’s left
is to allow users to change this data. In this section, you will implement a second activity that
allows users to add new to-do items, and after this you will also allow them to check off to-do
items to delete them.

To add the new activity, create a new package view.add and add a new activity to it by
right-clicking the package. Choose New, then Activity, and then Empty Activity. Name it
AddTodoActivity and let Android Studio generate the layout file activity_add_todo.xml.
Let’s first set up the layout. A simple LinearLayout as in Listing 7.37 will do for this activity.

Listing 7.37 Layout for AddTodoActivity


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"

8. https://developer.android.com/reference/android/support/v7/util/DiffUtil
234 Chapter 7 Android App Development with Kotlin: Kudoo App

android:orientation="vertical"
tools:context=".view.add.AddTodoActivity">

<EditText
android:id="@+id/etNewTodo"
android:hint="@string/enter_new_todo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_medium"
android:textAppearance="@android:style/TextAppearance.Medium"
tools:text="@string/enter_new_todo"
android:inputType="text" />

<Button
android:id="@+id/btnAddTodo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_to_do"
android:textAppearance="@android:style/TextAppearance"
android:layout_gravity="center_horizontal" />

</LinearLayout>

Add the missing string resources using Android Studio’s suggested actions or by editing res/
values/strings.xml. I used "Add to-do" and "Enter new todo…" as the values. Before
implementing the logic inside this new activity, let’s adjust the floating action button in
MainActivity—first its layout and then its click handler.

Instead of showing an email icon, the floating action button should have a simple plus icon. To
this end, navigate to res/drawable, right-click, choose New, and then Image Asset. Fill in the
required information:
 Icon Type: Action Bar and Tab Icons
 Name: ic_add
 Asset type: Clip Art
 Click on the Clip Art button, search for “add,” and select the simple plus icon
 Theme: HOLO_LIGHT

Click Next and then Finish. Now you can use this image asset in activity_main.xml by
replacing the existing app:srcCompat attribute, as shown in Listing 7.38.
App #1: Kudoo, a To-Do List App 235

Listing 7.38 Layout for the FloatingActionButton


<android.support.design.widget.CoordinatorLayout …>
<!-- … -->
<android.support.design.widget.FloatingActionButton …
app:srcCompat="@drawable/ic_add" />
</android.support.design.widget.CoordinatorLayout>

With the looks in place, it’s time to adjust the floating action button’s behavior. To this end, go
into MainActivity, remove the existing default click listener in onCreate, and introduce a new
setup function, as shown in Listing 7.39.

Listing 7.39 Setting Up the FloatingActionButton


// … (imports from before)
import android.content.Intent
import com.example.kudoo.view.add.AddTodoActivity

class MainActivity : AppCompatActivity() {


// …
private fun setUpFloatingActionButton() {
fab.setOnClickListener {
val intent = Intent(this, AddTodoActivity::class.java)
startActivity(intent) // Switches to AddTodoActivity
}
}
}

This helper method enables switching to the new activity when clicking the floating action
button. It sets up an intent to switch to the new AddTodoActivity, where users can then add a
new to-do. Now you can call this new setup method in onCreate, as shown in Listing 7.40.

Listing 7.40 Adjusting onCreate()


class MainActivity : AppCompatActivity() {
// …
override fun onCreate(savedInstanceState: Bundle?) {
// …
setUpRecyclerView()
setUpFloatingActionButton()
}
}
236 Chapter 7 Android App Development with Kotlin: Kudoo App

With this, the MainActivity is all set. So now it’s time to make the new AddTodoActivity
play its part. It has to use the text the user enters in order to store a new to-do into the database.
Listing 7.41 provides all code required for this activity.

Listing 7.41 Implementing the AddTodoActivity


import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import com.example.kudoo.R
import com.example.kudoo.db.DB
import com.example.kudoo.model.TodoItem
import com.example.kudoo.view.common.getViewModel
import com.example.kudoo.viewmodel.TodoViewModel
import kotlinx.android.synthetic.main.activity_add_todo.*
import kotlinx.coroutines.experimental.launch

class AddTodoActivity : AppCompatActivity() {

private lateinit var viewModel: TodoViewModel // Uses the view model as well

override fun onCreate(savedInstanceState: Bundle?) {


super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_todo)

viewModel = getViewModel(TodoViewModel::class)
setUpListeners()
}

private fun setUpListeners() { // Adds new to-do item to DB when clicking button
btnAddTodo.setOnClickListener {
val newTodo = etNewTodo.text.toString()
launch(DB) { viewModel.add(TodoItem(newTodo)) } // Initiates DB transaction
finish() // Switches back to MainActivity
}
}
}

The click listener for the Add to-do button first reads out the user’s text from the EditText and
then starts a new coroutine that stores the new to-do item to the database. Then the activity
App #1: Kudoo, a To-Do List App 237

finishes, causing the current activity to fade out so that the user gets back to the MainActivity,
where the to-do item automatically shows up thanks to LiveData.

Tip
When you test your app in the emulator, you may want to enable keyboard input to type a lot
faster in the emulator. If it’s not activated, open the AVD Manager in Android Studio, click on
the pen icon for the virtual device you’re using, click Show Advanced Settings, then scroll down
to the bottom and check Enable Keyboard Input.

This concludes the requirement to let users add their own to-do items. You can now run your
app, click on the plus to switch activities, enter your to-do item, and see it pop up in the
RecyclerView automatically. This is the power of Room working together with a LiveData
bound to a RecyclerView.

As a final touch, you may want to allow users to navigate up from the AddTodoActivity to the
MainActivity without entering a to-do item, and you can do so by making it a child activity
of MainActivity in your AndroidManifest.xml file. Listing 7.42 shows how to modify the
activity tag under application to achieve this.

Listing 7.42 Enabling Navigating Up from AddTodoActivity to MainActivity


<activity android:name=".view.add.AddTodoActivity"
android:parentActivityName=".MainActivity">

<meta-data android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.kudoo.MainActivity" />
</activity>

With this, you’ll see an arrow at the top of AddTodoActivity that allows users to go back
without entering a to-do item.

At this point, you may have lots of to-do items created in your app that you no longer want. So
the next and final step is to allow users to check off their to-do items, removing them from the
database and therefore from the list.

Enabling Checking Off To-Do Items


In this section, you’ll learn how to handle clicks on RecyclerView items to let users delete
completed to-do items by checking them off in the RecyclerView. First, the adapter must be
extended to receive a click handler that it can assign to the checkbox when binding to a view
holder. Listing 7.43 shows the required changes.
238 Chapter 7 Android App Development with Kotlin: Kudoo App

Listing 7.43 Assigning Event Handlers in the RecyclerView Adapter


class RecyclerListAdapter(
private val items: MutableList<TodoItem>,
private val onItemCheckboxClicked: (TodoItem) -> Unit
) : RecyclerView.Adapter<RecyclerListAdapter.ViewHolder>() {
// …
inner class ViewHolder(…) : … { // Note that this is now an ‘inner’ class

fun bindItem(todoItem: TodoItem) {


// …
cbTodoDone.setOnCheckedChangeListener { _, _ -> // Adds listener to check box
onItemCheckboxClicked(todoItem)
}
}
}
}

The adapter now accepts a click handler in its constructor, which must be a function that
accepts the selected TodoItem. This function is used in bindItem to assign a change listener to
the checkbox that is associated with the given to-do item. To easily access the onItemCheckbox-
Clicked property from the outer scope, make the ViewHolder an inner class.

Now, the MainActivity can pass in the desired event handler as in Listing 7.44.

Listing 7.44 Assigning Event Handlers in the RecyclerView Adapter


// … (imports from before)
import kotlinx.coroutines.experimental.android.UI

class MainActivity : AppCompatActivity() {


// …
private fun setUpRecyclerView() {
with(recyclerViewTodos) {
adapter = RecyclerListAdapter(mutableListOf(), onRecyclerItemClick())
// …
}
// …
}

private fun onRecyclerItemClick(): (TodoItem) -> Unit = { todo ->


launch(DB) { viewModel.delete(todo) }
}
}
Summary 239

The creation of the click handler is encapsulated into its own method so that every method
has a single responsibility. The click handler starts a coroutine to delete the TodoItem from the
database. So in this app, to-do items are immediately deleted once they are checked off.

This is all that’s required to make this use case work. You can now click the checkbox next to any
to-do item to delete that item from the database and, therefore, from the RecyclerView.

Summary
The app you created in this chapter covered many fundamental components and concepts from
both Kotlin and Android.
 First, you learned how Android Architecture Components (Room, ViewModel, and
LiveData) can facilitate setting up a database and handling lifecycles on Android.
 Next, you used the Kotlin Android Extensions to make your ViewHolder a
LayoutContainer and to avoid calling findViewById explicitly.
 You also saw how to implement a RecyclerView in Kotlin, and how to attach click
handlers to its list items.
 Lastly, throughout the app, you made use of Kotlin’s language features like data classes,
companion objects, and top-level declarations to solve tasks in a more concise and
idiomatic way.

With this, you are now able to implement basic apps for Android using Kotlin following
state-of-the-art tools and coding practices.
This page intentionally left blank
8
Android App Development
with Kotlin: Nutrilicious

The food you eat can be either the safest and most
powerful form of medicine or the slowest form of poison.
Ann Wigmore

After publication, the online code was updated for Kotlin 1.3. Go to informit.com/
title/9780134854199 and click on the Downloads tab for more information.

In this chapter, you’ll implement the “Nutrilicious” app: a more fleshed-out app that allows users
to explore food and nutrition data from the U.S. Department of Agriculture to make healthier diet
decisions. Figure 8.1 shows the finished app as you will create it in this chapter.

While developing this sample app, you will recap the fundamental concepts by getting more
practice. But you will also dive deeper and implement network access, map JSON data to domain
classes, map these domain classes to a Room database, and introduce a repository as the single
source of truth for data used in your app.

Figure 8.1 The final app allows users to search foods, choose favorites, and explore data
242 Chapter 8 Android App Development with Kotlin: Nutrilicious

Setting Up the Project


Every app is born by setting up a new Android project. The setup works the same way as before
except that this time you choose a Bottom Navigation Activity as your MainActivity.

Note
In case Android Studio’s project wizard changed, you can get the exact project template used in
this chapter from the app’s GitHub repository1 (just like the code for every step).

To adapt the template for this app, start by adjusting the bottom navigation. In res/menu/
navigation.xml, remove the last item titled Notifications—this app only needs two menu
items. Next, change the ID and title of the menu item titled Dashboard to represent a menu item
showing the user’s favorite foods. You can use Shift+F6 to rename the ID, or right-click, select
“Refactor,” and then click “Rename….” Listing 8.1 shows the resulting code for the bottom menu
in res/menu/navigation.xml.

Listing 8.1 Bottom Navigation Menu


<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/navigation_home"
android:icon="@drawable/ic_home_black_24dp"
android:title="@string/title_home" />
<item android:id="@+id/navigation_my_foods"
android:icon="@drawable/ic_dashboard_black_24dp"
android:title="@string/title_my_foods" />
</menu>

The res/values/strings.xml file must contain the used string resource, as in Listing 8.2.

Listing 8.2 String Resources for Bottom Navigation Menu


<string name="title_home">Home</string>
<string name="title_my_foods">My Foods</string>

Next, remove unnecessary code from MainActivity, namely the when case for the Notifications
menu item in the navigation listener. Also, adjust the when case for the Dashboard item to your
new My Foods item. You may also want to rename the listener to a more concise name. Your
resulting code should be similar to Listing 8.3.

1. https://github.com/petersommerhoff/nutrilicious-app
Adding a RecyclerView to the Home Screen 243

Listing 8.3 MainActivity Setup for the Bottom Navigation Menu


class MainActivity : AppCompatActivity() {

private val navListener = BottomNavigationView.OnNavigationItemSelectedListener {


when(it.itemId) {
R.id.navigation_home -> { // Defines action for when ‘Home’ is clicked
return@OnNavigationItemSelectedListener true
}
R.id.navigation_my_foods -> { // Defines action for when ‘My Foods’ is clicked
return@OnNavigationItemSelectedListener true
}
}
false
}
// …
}

Clicking the menu items doesn’t do much yet but it will change which item is shown as being
active because you’re returning true from the listener—you may set the text of the TextView
according to the selected item at this point, but that TextView will be replaced in the next step.
Running the app should present you with a simple text view and the bottom navigation with two
items that indicate correctly which one was selected. This is the basic template that you can build
on for the remainder of this chapter.

Adding a RecyclerView to the Home Screen


As in many apps, especially ones that present data, the centerpiece of this app is a
RecyclerView. It will show all foods that were found for a user query. The setup follows
the same steps as always.
 Define the layout for the activity containing the RecyclerView.
 Define the layout for each list item in the RecyclerView.
 Implement the adapter that provides data for the RecyclerView.
 Set up the RecyclerView in the activity that shows it.

Layout for MainActivity


The MainActivity layout will consist of the RecyclerView covering the screen and, of course,
the bottom navigation. To this end, replace the TextView in activity_main.xml with the
244 Chapter 8 Android App Development with Kotlin: Nutrilicious

recycler view, as shown in Listing 8.4. Also, add scrolling behavior to the ConstraintLayout so
that the list of foods is scrollable.

Listing 8.4 MainActivity Layout


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
app:layout_behavior="@string/appbar_scrolling_view_behavior" …>

<android.support.v7.widget.RecyclerView
android:id="@+id/rvFoods"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/navigation" />

<android.support.design.widget.BottomNavigationView
android:id="@+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
android:background="?android:attr/windowBackground"
app:menu="@menu/navigation" />

</android.support.constraint.ConstraintLayout>

Here, a layout behavior was added to the ConstraintLayout so that the view can be scrolled
when the RecyclerView overflows the screen, and the recycler view was added. Also, the
BottomNavigationView layout was simplified.

Layout for RecyclerView Items


In res/layout, add a new layout resource file titled rv_item.xml that represents a list item. It
shows a food name with a short description and an image view containing a star that will be used
to indicate favorite foods. Listing 8.5 implements this layout using a ConstraintLayout that
aligns the text views below each other, with the image view to their right.

Listing 8.5 Layout for RecyclerView Items


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
Adding a RecyclerView to the Home Screen 245

xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/medium_padding">

<TextView
android:id="@+id/tvFoodName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/medium_font_size"
app:layout_constraintRight_toLeftOf="@id/ivStar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Gingerbread" />

<TextView
android:id="@+id/tvFoodType"
tools:text="Sweets and Candy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/tvFoodName"
app:layout_constraintStart_toStartOf="@id/tvFoodName"
app:layout_constraintRight_toLeftOf="@id/ivStar"
android:textColor="@color/lightGrey"
android:textSize="@dimen/small_font_size" />

<ImageView
android:id="@+id/ivStar"
android:layout_width="32dp"
android:layout_height="32dp"
android:contentDescription="@string/content_description_star"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>
246 Chapter 8 Android App Development with Kotlin: Nutrilicious

Note that you can use tools:text to show a given text in Android Studio’s Design View and
make the layout come to life. All attributes from the tools namespace are used only for tooling,
typically for Android Studio’s Design View; they don’t affect the app at runtime. This layout again
requires several new resources, and they are given in Listing 8.6.

Listing 8.6 Resources for RecyclerView Item Layout


// In res/values/strings.xml
<string name="content_description_star">favorite</string>

// In res/values/dimens.xml
<dimen name="tiny_padding">4dp</dimen>
<dimen name="medium_padding">8dp</dimen>
<dimen name="medium_font_size">16sp</dimen>
<dimen name="small_font_size">13sp</dimen>

// In res/values/colors.xml
<color name="lightGrey">#888888</color>

With this, all layouts are ready. So now it’s time to write some Kotlin code!

Implementing the Food Model


To implement the adapter, it is useful to have a model that encapsulates the data shown in each
list item. This app shows foods, so you need a Food class. Thanks to data classes, this is easy to do.
Create a new package model and add a data class Food to it, as in Listing 8.7. This one line of code
is all you need for your models, for now.

Listing 8.7 Food Data Class


data class Food(val name: String, val type: String, var isFavorite: Boolean = false)

Implementing the RecyclerView Adapter


As always, implementing the recycler view adapter requires overriding the class RecyclerView.
Adapter<YourViewHolder> and overriding the three methods onCreateViewHolder,
onBindViewHolder, and getItemCount. Remember, you can make the ViewHolder a
LayoutContainer to use the Kotlin Android Extensions there as well. For this purpose, enable
experimental extensions at the very bottom of your module’s build.gradle file, as shown in
Listing 8.8.
Adding a RecyclerView to the Home Screen 247

Listing 8.8 Enabling the Experimental Kotlin Android Extensions


androidExtensions {
experimental = true
}

With this setup, you can follow the same structure as in Kudoo to implement the adapter. Create a
new package view.main and add a new class SearchListAdapter to it. This will be the adapter
for the list. Also, move the MainActivity into this new package because it’s intended to contain
everything related to the MainActivity. Listing 8.9 shows the code for the adapter, which is
very similar to the one in the Kudoo app. Try to implement it yourself first to see if you stumble
upon any roadblocks.

Listing 8.9 RecyclerView Adapter


import android.support.v7.widget.RecyclerView
import android.view.*
import com.example.nutrilicious.R
import com.example.nutrilicious.model.Food
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.rv_item.* // Imports synthetic properties

class SearchListAdapter(
private var items: List<Food> // Uses a read-only list of items to display
) : RecyclerView.Adapter<ViewHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {


val view = LayoutInflater.from(parent.context) // Inflates layout to create view
.inflate(R.layout.rv_item, parent, false)
return ViewHolder(view) // Creates view holder that manages the list item view
}

override fun getItemCount(): Int = items.size

override fun onBindViewHolder(holder: ViewHolder, position: Int) {


holder.bindTo(items[position]) // Binds data to a list item
}

// In this app, we'll usually replace all items so DiffUtil has little use
fun setItems(newItems: List<Food>) {
248 Chapter 8 Android App Development with Kotlin: Nutrilicious

this.items = newItems // Replaces whole list


notifyDataSetChanged() // Notifies recycler view of data changes to re-render
}

inner class ViewHolder(


override val containerView: View
) : RecyclerView.ViewHolder(containerView), LayoutContainer {

fun bindTo(food: Food) { // Populates text views and star image to show a food
tvFoodName.text = food.name
tvFoodType.text = food.type

val image = if (food.isFavorite) {


android.R.drawable.btn_star_big_on
} else {
android.R.drawable.btn_star_big_off
}
ivStar.setImageResource(image)
}
}
}

In addition to the basic overrides that every adapter needs and the custom ViewHolder class,
this implementation already provides a setItems method to update the list of items shown in
the RecyclerView—this method will be used later. Also, the ViewHolder already displays the
correct ImageView, depending on whether the food is a favorite or not. As you can see, these
adapters always follow the same basic structure.

Adding the RecyclerView to MainActivity


Setting up the RecyclerView in your activities also works mostly the same way every time. For
now, the adapter is populated with hard-coded sample data again to see if the layout and adapter
work as expected. Listing 8.10 encapsulates the RecyclerView setup into a method.

Listing 8.10 Setting Up the RecyclerView in MainActivity


import android.support.v7.widget.*

class MainActivity : AppCompatActivity() {


// …
private fun setUpSearchRecyclerView() = with(rvFoods) {
Adding a RecyclerView to the Home Screen 249

adapter = SearchListAdapter(sampleData())
layoutManager = LinearLayoutManager(this@MainActivity)
addItemDecoration(DividerItemDecoration(
this@MainActivity, LinearLayoutManager.VERTICAL
))
setHasFixedSize(true)
}
}

As sample data, you can use a list of sample foods as in Listing 8.11. Note that this sample data is
used only temporarily. Generally, you should not use hard-coded strings in your Android app and
use string resources instead. Here, we skip this step for the sake of brevity.

Listing 8.11 Hard-Coding the Sample Data


import com.example.nutrilicious.model.Food

class MainActivity : AppCompatActivity() {


// …
private fun sampleData() = listOf( // Only temporary sample data, thus hard-coded
Food("Gingerbread", "Candy and Sweets", false),
Food("Nougat", "Candy and Sweets", true),
Food("Apple", "Fruits and Vegetables", false),
Food("Banana", "Fruits and Vegetables", true)
)
}

Finally, call the setup method in onCreate, as in Listing 8.12.

Listing 8.12 Calling the Setup Method in onCreate


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setUpSearchRecyclerView()
navigation.setOnNavigationItemSelectedListener(navListener)
}
250 Chapter 8 Android App Development with Kotlin: Nutrilicious

With this, the MainActivity should present the sample foods just like it will in the final app. It
should also indicate two of the items as favorites with an active star, as shown in Figure 8.2.

Figure 8.2 Nutrilicious app with a working RecyclerView and hard-coded sample data

Because it always follows a similar process, implementing a RecyclerView is straightforward


once you have done it a few times. Even if you were not familiar with it before, I hope these two
examples took the magic out of the implementation of RecyclerViews.

Fetching Data from the USDA Nutrition API


The next major step towards the final app is to actually fetch food data. The U.S. Department of
Agriculture (USDA) provides an open API2 to access its extensive database of food and nutrition
details. There are two endpoints that you will use in this app.
 The Search API to search for foods that match the user’s input
 Documentation: https://ndb.nal.usda.gov/ndb/doc/apilist/API-SEARCH.md
 Endpoint: https://api.nal.usda.gov/ndb/search/
 The Details API to retrieve the nutrient amounts for foods
 Documentation: https://ndb.nal.usda.gov/ndb/doc/apilist/API-FOOD-REPORTV2.md
 Endpoint: https://api.nal.usda.gov/ndb/V2/reports/

To use this API, you need to get a free API key from https://ndb.nal.usda.gov/ndb/doc/index by
clicking on the Sign up now link in the middle of the page and entering your information.

2. https://ndb.nal.usda.gov/ndb/doc/index
Fetching Data from the USDA Nutrition API 251

For this app, you will use OkHttp3 to access the network, Retrofit4 to access the API endpoints,
and Moshi5 to map the JSON data to Kotlin objects. As always, the first step is to add the corre-
sponding dependencies to your module’s build.gradle file as in Listing 8.13.

Listing 8.13 Gradle Dependencies for Network and API Access


dependencies {
// …
def retrofit_version = "2.4.0"
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"

def okhttp_version = "3.6.0"


implementation "com.squareup.okhttp3:logging-interceptor:$okhttp_version"
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
}

Using Retrofit
With the dependencies in place, the next step is to initialize Retrofit to make API calls. As a
first step, add a new package data.network that contains all network-related code. Inside this
package, add a new file HttpClient.kt that will use OkHttp and Retrofit to set up a Retrofit
object that acts as the HTTP client for this app.

At the top of the file, add the constants that you will need inside this file, as in Listing 8.14. For
this app, you need the API key that you received from the USDA website and the base URL.

Listing 8.14 Constants Used for Retrofit


import com.example.nutrilicious.BuildConfig

private const val API_KEY = BuildConfig.API_KEY


private const val BASE_URL = "https://api.nal.usda.gov/ndb/"

As you can see, the API key will come from Gradle’s BuildConfig. To set this up, you first have to
add the key to your personal Gradle properties, located in the .gradle folder in your user direc-
tory. The typical locations are
 On Windows: C:\Users\<USERNAME>\.gradle\gradle.properties
 On Mac: /Users/<USERNAME>/.gradle/gradle.properties
 On Linux: /home/<USERNAME>/.gradle/gradle.properties

3. https://github.com/square/okhttp
4. https://github.com/square/retrofit
5. https://github.com/square/moshi
252 Chapter 8 Android App Development with Kotlin: Nutrilicious

You may have to create the gradle.properties file (if it doesn’t exist yet), then add the key to it
as shown in Listing 8.15.

Listing 8.15 Adding API Keys to Your Gradle Properties


Nutrilicious_UsdaApiKey = "<YOUR_API_KEY_HERE>"

You can name the property as you like. Here, the project name is used as a prefix so that proper-
ties are grouped by project. The next step is to add this property to the project’s build configura-
tion. To this end, go to your module’s build.gradle file and under buildTypes, and add a new
debug build type that makes the key accessible in the project in debug builds. This is shown in
Listing 8.16.

Listing 8.16 Adding a Build Config Field


buildTypes {
debug {
buildConfigField 'String', "API_KEY", Nutrilicious_UsdaApiKey // From properties
}
release { … }
}

This makes the API key accessible as BuildConfig.API_KEY from anywhere in the project in
debug builds (running the app normally from Android Studio always triggers a debug build). You
can add the same build config field to the release build type if you want to use the same API key
in release builds. After Gradle finishes syncing the project, the API key should be available and
allow the assignment from Listing 8.14 that accesses BuildConfig.API_KEY.

The next step in HttpClient.kt is to start building the Retrofit object using its builder. From
there, you’ll go down the rabbit hole to construct all the required objects it uses, until you’re
finally building the complete Retrofit instance with all its dependencies. Listing 8.17 starts off
with the function to construct the actual object. Like all other functions in HttpClient.kt, it’s
declared on the file level.

Listing 8.17 Building the Retrofit Object


import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
// …
private fun buildClient(): Retrofit = Retrofit.Builder() // Builds Retrofit object
.baseUrl(BASE_URL)
.client(buildHttpClient())
.addConverterFactory(MoshiConverterFactory.create()) // Uses Moshi for JSON
.build()
Fetching Data from the USDA Nutrition API 253

The base URL is already declared and the MoshiConverterFactory comes from the dependency
to retrofit2:converter-moshi. But building the HTTP client is still to be done using OkHttp.
This way, Retrofit relies on OkHttp for the actual HTTP request, and OkHttp allows adding
interceptors to perform logging, add the API key to the query, and more. Listing 8.18 sets up an
OkHttp client that does exactly that.

Listing 8.18 Building the HTTP Client


import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
// …
private fun buildHttpClient(): OkHttpClient = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor()) // Logs API responses to Logcat
.addInterceptor(apiKeyInterceptor()) // Adds API key to request URLs
.build()

As you can see, OkHttp makes it easy to set timeouts and attach interceptors. In this app, you’ll
use a logging interceptor that logs any request results to the Logcat and an interceptor that injects
the API key into the request URL.

Creating the interceptors is the last step to finally build the entire Retrofit object. Listing 8.19
implements the logging interceptor.

Listing 8.19 Building the HTTP Client


import okhttp3.logging.HttpLoggingInterceptor
// …
private fun loggingInterceptor() = HttpLoggingInterceptor().apply {
level = if (BuildConfig.DEBUG) {
HttpLoggingInterceptor.Level.BODY // Only does logging in debug mode
} else {
HttpLoggingInterceptor.Level.NONE // Otherwise no logging
}
}

The HTTPLoggingInterceptor from OkHttp already implements the basic logic. All that’s left
to do is to set the appropriate logging level for development and production. This is done using
the predefined BuildConfig.DEBUG flag so that logging is only performed during development.
Note that the apply function allows declaring this function as a single expression.
254 Chapter 8 Android App Development with Kotlin: Nutrilicious

The next interceptor adds the API key as a query parameter to the URL. Listing 8.20 encapsulates
the setup of this kind of interceptor into a separate function.

Listing 8.20 Encapsulating Interceptor Creation


import okhttp3.Interceptor
// …
private fun injectQueryParams(
vararg params: Pair<String, String>
): Interceptor = Interceptor { chain ->

val originalRequest = chain.request()


val urlWithParams = originalRequest.url().newBuilder()
.apply { params.forEach { addQueryParameter(it.first, it.second) } }
.build()
val newRequest = originalRequest.newBuilder().url(urlWithParams).build()

chain.proceed(newRequest)
}

Because Interceptor is a SAM interface coming from Java, you can use Kotlin’s SAM conversions
to create an interceptor with lambda syntax. This implicitly overrides Interceptor.inter-
cept to intercept the request chain and add query parameters to it. The details of this method are
OkHttp-specific, but notice how apply can be extremely useful in combination with builder-style
methods.

Using this helper function, setting up the remaining interceptor is a matter of passing in the
query parameter for the API key, as shown in Listing 8.21.

Listing 8.21 Creating the Interceptor that Adds the API Key to the Query
private fun apiKeyInterceptor() = injectQueryParams(
"api_key" to API_KEY
)

Due to the use of Pair in the helper function, the definition of query parameters becomes clean
and readable using Kotlin’s to function. It would be trivial to add or remove additional query
parameters or to create another interceptor without duplicating code.

You’re now out of the rabbit hole and the buildClient function is able to create a Retrofit
object. The next step is to call it to create an object that is used to access the Search API. With
Retrofit, you first need an interface that defines any requests to make to an endpoint based on the
Fetching Data from the USDA Nutrition API 255

base URL. Listing 8.22 defines the interface needed to access the Search API. Place this into a new
file UsdaApi in the data.network package.

Listing 8.22 Defining the Retrofit Interface to Access the Search API
import okhttp3.ResponseBody
import retrofit2.Call
import retrofit2.http.*

interface UsdaApi {

@GET("search?format=json") // Is appended to the base URL


fun getFoods(
@Query("q") searchTerm: String, // Only non-optional parameter
@Query("sort") sortBy: Char = 'r', // Sorts by relevance by default
@Query("ds") dataSource: String = "Standard Reference",
@Query("offset") offset: Int = 0
): Call<ResponseBody> // Allows to retrieve raw JSON for now
}

The @GET annotation indicates that this performs a GET request and its argument will be
appended to the base URL, resulting in the endpoint for the Search API. The @Query annotation
indicates that the argument is appended to the URL as the value for the corresponding query
parameter, such as q or sort. Thus, the final request URL is built based on the BASE_URL, the
@GET suffix, the query parameter from @Query, and any query parameters from interceptors.
A resulting request URL has the following form:

https://api.nal.usda.gov/ndb/search?format=json&q=raw&api_key=<YOUR_API_KEY>&sort=r
&ds=Standard%20Reference&offset=0.

The Call<T> in the return value is Retrofit’s implementation of a future. Like every future, it
wraps the result of the asynchronous call, which is performed off the main thread. Here, you
parse the result into an OkHttp ResponseBody to fetch the raw JSON data and see if the request
itself works—mapping the JSON result to domain classes is done in the next section.

To finish the infrastructure for API requests, you just need to build the Retrofit object and then
use it to create an implementation of this interface. Listing 8.23 demonstrates how to do this in
the HttpClient.kt file.

Listing 8.23 Building the Search API Object


private val usdaClient by lazy { buildClient() }
val usdaApi: UsdaApi by lazy { usdaClient.create(UsdaApi::class.java) } // Public
256 Chapter 8 Android App Development with Kotlin: Nutrilicious

Note that the usdaApi object is the only declaration in HttpClient.kt that is exposed to the
outside—all other declarations are private and define the internal details of how this object is
created. Because this object is expensive to create, its initialization is deferred using lazy. This
also makes sure it is instantiated only once and then cached.

Performing API Requests


You can use the usdaApi object to perform the API request. To test your setup, you can perform a
temporary test request in MainActivity. But first, you’ll need to add the internet permission to
your app and include Kotlin’s coroutines to perform the network request in a background thread.
First, in your AndroidManifest.xml, add the permission as in Listing 8.24.

Listing 8.24 Enabling Internet Access


<manifest …>
<uses-permission android:name="android.permission.INTERNET" />
<application …>…</application>
</manifest>

To perform asynchronous network requests, add the coroutine dependencies in your module’s
Gradle build file as in Listing 8.25.

Listing 8.25 Gradle Dependencies for Kotlin Coroutines


def coroutines_version = "0.24.0" // Latest version may differ slightly in use
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"

As a final preparation step, create a dedicated coroutine dispatcher for network calls. For this, add
a new file NetworkDispatcher into the data.network package. Inside it, you can declare a
thread pool using two threads for network access, as in Listing 8.26.

Listing 8.26 Coroutine Dispatcher for Network Calls


import kotlinx.coroutines.newFixedThreadPoolContext

val NETWORK = newFixedThreadPoolContext(2, "NETWORK") // Dedicated network context

With this, you can now perform a test request in MainActivity.onCreate to check your setup.
Thanks to the logging interceptor, you can observe the request and its results in Android Studio’s
Logcat. Listing 8.27 demonstrates how to use the usdaApi. This use is just temporary and of
course not encouraged in production. Later, you will again incorporate view models for asynchro-
nous requests.
Mapping JSON Data to Domain Classes 257

Listing 8.27 Performing an API Request


import com.example.nutrilicious.data.network.*
import com.example.nutrilicious.model.Food
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.launch
// …
class MainActivity : AppCompatActivity() { // …
override fun onCreate(savedInstanceState: Bundle?) {
// …
launch(NETWORK) {
usdaApi.getFoods("raw").execute() // Logs results to Logcat due to interceptor
}
}
}

This calls the getFoods method from the UsdaApi interface and executes the asynchronous
Call<T> in the background to perform the request. When you run the app, you should now see
logging entries from OkHttp and see the retrieved JSON data. You can open Logcat using Alt+6 or
from the bottom toolbar and search for “OkHttp” to filter out the other entries.

Note
There’s an issue in Android Studio at the time of writing where Apply Changes does not play
well with coroutines. If you get an error “CoroutineImpl.label is inaccessible..., ”
try re-running the app normally without using Apply Changes (using Shift+F10 or Ctrl+R
on Mac).

You’re now able to perform network requests using Retrofit to fetch data from the USDA in JSON
format. Of course, users do not want to see raw JSON data. So in the following section, you will
map the data to domain classes.

Mapping JSON Data to Domain Classes


At this point, you have your raw JSON data and your model class Food. So let’s map the JSON
data to the Food class in order to work with it in the app. The typical approach using Moshi is
to first parse the JSON data into data transfer objects (DTOs) and then map those to your model
classes.

Moshi officially supports Kotlin, meaning it knows how to handle things like primary construc-
tors and properties when mapping to classes. For Moshi itself, add the dependencies from
Listing 8.28 to the module’s build script.
258 Chapter 8 Android App Development with Kotlin: Nutrilicious

Listing 8.28 Gradle Dependencies for Moshi


def moshi_version = "1.6.0"
implementation "com.squareup.moshi:moshi:$moshi_version"
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"

To map JSON data to your DTOs, Moshi can either use reflection or code generation. In
this app, you use code generation—thus the moshi-kotlin-codegen dependency. Using
reflection, you would transitively depend on kotlin-reflect, which adds over 2MB and
around 16K methods to your Android PacKage (APK)6 (without ProGuard) so I’d recommend
avoiding it.

The code generation approach relies on annotations (like Room). So you need to apply the
kotlin-kapt plugin for annotation processing at the top of your build.gradle file, as shown
in Listing 8.29.

Listing 8.29 Enabling the Kotlin Annotation Processor


apply plugin: 'kotlin-kapt'

Mapping JSON to DTOs


To let Moshi map the JSON data to a DTO class, the DTO must replicate the structure of the JSON
data. More specifically, they must use the same property names to indicate that a property should
be populated with the data from the corresponding JSON field.

Tip
To write down your DTOs, I’d recommend creating a .json file in your project that
contains sample data from the API. You can use it to explore the structure and write
your DTOs. In Android Studio, you can create a Sample Data Directory and place it
there.
You can simply copy and paste the data from your browser7 and then use Code, Reformat
Code from the menu to fix the code formatting in Android Studio if necessary.
Once you have the file, you can use Window, Editor Tabs, then Split Vertically from the
menu to open another editor. This way, you can open the .json file in one of them and
write down your DTO alongside it. This makes it easier to map the structure
correctly.

For the Search API, the returned JSON data has the format shown in Listing 8.30.

6. Android PacKage is the distribution format for Android apps.


7. https://api.nal.usda.gov/ndb/search/?format=json&ds=Standard%20Reference&q=raw&sort=r&
max=10&api_key=DEMO_KEY
Mapping JSON Data to Domain Classes 259

Listing 8.30 Search API JSON Format


{
"list": {
"q": "raw",
"ds": "Standard Reference",
"sort": "r",

"item": [
{
"offset": 0,
"group": "Vegetables and Vegetable Products", // Type of the food
"name": "Coriander (cilantro) leaves, raw", // Food title
"ndbno": "11165", // Unique identifier
"ds": "SR",
"manu": "none"
},
// More items here…
]
}
}

The actual data you need is nested into the object’s list property, which again wraps it into
an item property. Thus, you need to create wrapper DTOs that, intuitively speaking, navigate
down that hierarchy. To do so, add a new package data.network.dto and add a new file
SearchDtos.kt to it. Listing 8.31 shows the definition of the wrapper types to navigate down
the list and item properties.

Listing 8.31 Wrapper DTOs for the Search API


import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
class ListWrapper<T> {
var list: T? = null // Navigates down the ‘list’ object
}

@JsonClass(generateAdapter = true)
class ItemWrapper<T> {
var item: T? = null // Navigates down the ‘item’ array
}

typealias SearchWrapper<T> = ListWrapper<ItemWrapper<T>>


260 Chapter 8 Android App Development with Kotlin: Nutrilicious

Moshi will map the JSON list property to the corresponding field in the ListWrapper class,
and because you will use ListWrapper<ItemWrapper<T>>, the JSON item property will be
mapped to the property in ItemWrapper. The typealias allows using a more concise syntax
when using the DTOs. Because the two wrappers are never used separately, you only need the
SearchWrapper<T> outside of this file. The @JsonClass annotations tell Moshi to include these
classes in the JSON mapping process.

Now that the wrappers navigate to the actual data, you can add the DTO that contains this data,
the FoodDto. Listing 8.32 shows its declaration, which also belongs to SearchDtos.kt.

Listing 8.32 Food DTO for the Search API


@JsonClass(generateAdapter = true)
class FoodDto { // Uses lateinit for properties that must be populated by Moshi
lateinit var ndbno: String
lateinit var name: String
lateinit var group: String
}

There are more properties available in the JSON data, but for this app you only need the
nutrition database number (NDBNO) that uniquely identifies a food, its name, and its group
(its category). Using late-initialized properties, you can avoid creating nullables here. This is
a great use case for lateinit, namely when there is a library or tool that is responsible for
initialization. Note that the NDBNO is declared as a String because it may be left-padded
with zeros, and while the API finds a food for the NDBNO "09070", it will not find one for
"9070". The NDBNO will be used later to retrieve details about a specific food.

Now that you have mapped the JSON data to DTOs, you can tell Retrofit to return a DTO from its
asynchronous call. It then uses Moshi to perform the mapping. So in UsdaApi.kt, update the
return type as shown in Listing 8.33.

Listing 8.33 Returning the DTO from Retrofit Calls


import com.example.nutrilicious.data.network.dto.*

fun getFoods(@Query("q") searchTerm: String, …): Call<SearchWrapper<List<FoodDto>>>

Mapping DTOs to Models


Mapping the DTOs to your domain classes is straightforward and can be done by a secondary
constructor in the models. Listing 8.34 shows how to do it for the Food class.

Here, the model was extended with an id and a secondary constructor that performs the
mapping from a DTO. In this example, the mapping is just a matter of renaming properties. This
is one of the typical tasks when mapping to your models because DTOs typically use the property
names given in the JSON data.
Mapping JSON Data to Domain Classes 261

Listing 8.34 Secondary Constructor to Map DTO to Model


data class Food(
val id: String, // New property
val name: String,
val type: String,
var isFavorite: Boolean = false
) {
constructor(dto: FoodDto) : this(dto.ndbno, dto.name, dto.group) // Maps from DTO
}

For simple mappings like this, you don’t necessarily have to separate your DTO and model.
Instead, you could use @SerializedName to specify the JSON names and then choose differ-
ent property names. Here, we use separate DTOs consistently to illustrate the process. In general,
more complex transformations of the data may be required. In these cases, you should separate
your DTOs from the models.

You are now all set to map the raw JSON data to the classes you actually want to use in your app,
so you can call it again in the MainActivity to sanity-check the mapping. Because the data is
eventually represented as a List<Food>, you can easily populate the RecyclerView with it to
display the API data in the app. In Listing 8.35, MainActivity.onCreate is adjusted accord-
ingly. Again, this is just temporary use. You should not perform asynchronous requests from
onCreate like this in production because of potential memory leaks.

Listing 8.35 Displaying Mapped Data in RecyclerView


import kotlinx.coroutines.android.UI
import kotlinx.coroutines.withContext
// …
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// …
launch(NETWORK) {
val dtos = usdaApi.getFoods("raw").execute()?.body()?.list?.item!!
val foods: List<Food> = dtos.map(::Food) // Maps all DTOs to Food models

withContext(UI) { // Must use main thread to access UI elements


(rvFoods.adapter as SearchListAdapter).setItems(foods)
}
}
}
}
262 Chapter 8 Android App Development with Kotlin: Nutrilicious

This executes the request and accesses the data stored inside wrappers. After that, it maps the list
of DTOs to a list of models by calling the Food constructor with each DTO. In the UI thread, this
food list can then be shown in the RecyclerView by passing it to the adapter.

At this point, you can remove the sampleData and initialize the adapter with an empty list
instead. It will then be populated when the request data is ready. Listing 8.36 shows the necessary
adjustment in setUpRecyclerView.

Listing 8.36 Removing the Sample Data


adapter = SearchListAdapter(emptyList())

When you run the app now, it should fetch JSON data from the USDA API, map it to your
domain models, and display it in the RecyclerView. With this, the barebones app functional-
ity is already in place. However, performing the asynchronous request from onCreate is not safe
because it is not lifecycle aware and the call remains active even if the activity is destroyed, poten-
tially causing memory leaks. So before you extend the existing functionality, let’s refactor the
architecture to avoid asynchronous calls directly from MainActivity—you already know how
this is done.

Introducing a ViewModel for Search


The MainActivity should get its data from a view model. This automatically allows lifecycle-
aware asynchronous requests across configuration changes, in addition to cleaning up the archi-
tecture. Let’s include the dependencies for all required Android Architecture Components already
as they will be used later. Listing 8.37 shows the dependencies.

Listing 8.37 Gradle Dependencies for Architecture Components


def room_version = "1.1.0"
implementation "android.arch.persistence.room:runtime:$room_version"
kapt "android.arch.persistence.room:compiler:$room_version"

def lifecycle_version = "1.1.1"


implementation "android.arch.lifecycle:extensions:$lifecycle_version"

Now add a new package viewmodel and add a new file SearchViewModel.kt. This view model
can extend the ViewModel class instead of AndroidViewModel because it does not require the
application context. It provides a clean interface for all calls to the Search API. To do so, it uses a
helper function to perform to actual call, as shown in Listing 8.38.

This executes the API request, reads the response, and navigates down through the list and
item properties of the SearchWrapper. The elvis operator handles not only the case that doRe-
quest or any property returns null but also the case that an exception occurs. In both cases, the
method returns an empty list.
Introducing a ViewModel for Search 263

Listing 8.38 Step 1: Implementing the SearchViewModel


import android.arch.lifecycle.ViewModel
import com.example.nutrilicious.data.network.dto.*
import retrofit2.Call

class SearchViewModel : ViewModel() {

private fun doRequest(req: Call<SearchWrapper<List<FoodDto>>>): List<FoodDto> =


req.execute().body()?.list?.item ?: emptyList()
}

The view model uses this helper to implement a suspending function that performs the asynchro-
nous call using withContext (that can return back a result) as in Listing 8.39.

Listing 8.39 Step 2: Implementing the SearchViewModel


import com.example.nutrilicious.data.network.*
import com.example.nutrilicious.model.Food
import kotlinx.coroutines.withContext

class SearchViewModel : ViewModel() {

suspend fun getFoodsFor(searchTerm: String): List<Food> { // Fetches foods from API


val request: Call<SearchWrapper<List<FoodDto>>> = usdaApi.getFoods(searchTerm)
val foodDtos: List<FoodDto> = withContext(NETWORK) { doRequest(request) }
return foodDtos.map(::Food)
}
// …
}

This performs the asynchronous call using the network dispatcher and maps all retrieved DTOs to
Food objects. With the view model in place, you can now reference it in MainActivity as usual,
using a late-initialized property that is initialized in onCreate, as done in Listing 8.40.

Listing 8.40 Using the SearchViewModel in MainActivity


import com.example.nutrilicious.view.common.getViewModel // Created next

class MainActivity : AppCompatActivity() {

private lateinit var searchViewModel: SearchViewModel


// …
override fun onCreate(savedInstanceState: Bundle?) {
264 Chapter 8 Android App Development with Kotlin: Nutrilicious

// …
navigation.setOnNavigationItemSelectedListener(navListener)
searchViewModel = getViewModel(SearchViewModel::class)
// …
}
// …
}

The getViewModel function is defined in a ViewExtensions.kt file as in the Kudoo app. You
can simply copy and paste the view.common package to this project (as well as future projects).
Listing 8.41 recaps this useful extension.

Listing 8.41 getViewModel Extension Function


import android.arch.lifecycle.*
import android.support.v4.app.FragmentActivity
import kotlin.reflect.KClass

fun <T : ViewModel> FragmentActivity.getViewModel(modelClass: KClass<T>): T {


return ViewModelProviders.of(this).get(modelClass.java)
}

Finally, remove the asynchronous call in onCreate and therefore the dependency to usdaApi
from your MainActivity and use the view model instead, as in Listing 8.42.

Listing 8.42 Using the SearchViewModel in MainActivity


override fun onCreate(savedInstanceState: Bundle?) {
// …
searchViewModel = getViewModel(SearchViewModel::class)

launch(NETWORK) { // Uses network dispatcher for network call


val foods = searchViewModel.getFoodsFor("raw")

withContext(UI) { // Populates recycler view with fetched foods (on main thread)
(rvFoods.adapter as SearchListAdapter).setItems(foods)
}
}
}

This is all that is required to set up this view model in your project. Now the MainActivity only
has a reference to its view model that provides it with all the data it needs, and in a lifecycle-aware
way. The app should still show the results fetched for “raw” when launched.
Letting Users Search Foods 265

Letting Users Search Foods


As a next step, the app should display what the user searches using a search field at the top. In
other words, it should make an API request whenever the user issues a search. First, you should
encapsulate the logic for a request into a method in MainActivity, as in Listing 8.43.

Listing 8.43 Encapsulating the Logic For Requests


private fun updateListFor(searchTerm: String) {
launch(NETWORK) {
val foods = searchViewModel.getFoodsFor(searchTerm)

withContext(UI) {
(rvFoods.adapter as SearchListAdapter).setItems(foods)
}
}
}

This method still contains the launch call to perform the API request on a background thread so
updateListFor is not a suspending function and can therefore be called from outside a corou-
tine. Next, remove the test request in onCreate. Instead, you’ll implement a proper Android
Search Interface8 that lets users search the foods in which they are interested.

Implementing a Search Interface


The first step is to add a menu resource representing the search bar that will be displayed at the
top of the screen. For this, add a new menu resource res/menu/search_menu.xml, and set it up
as in Listing 8.44 with just a single menu item of type SearchView.

Listing 8.44 Menu Resource for Search Interface


<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">

<item android:id="@+id/search"
android:title="@string/search_title"
android:icon="@android:drawable/ic_menu_search"
app:showAsAction="always"
app:actionViewClass="android.widget.SearchView" />
</menu>

8. https://developer.android.com/training/search/setup
266 Chapter 8 Android App Development with Kotlin: Nutrilicious

This uses a new string resource, shown in Listing 8.45.

Listing 8.45 String Resource for Search Menu


<string name="search_title">Search food…</string>

Next, you need a so-called searchable configuration that defines the behavior of the search view.
This is defined in a new resource file res/xml/searchable.xml, as in Listing 8.46.

Listing 8.46 Searchable Configuration


<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/app_name"
android:hint="@string/search_title" />

The label must be the same as the label of the application tag from your AndroidManifest.
xml file, thus using @string/app_name. Also, the hint helps users know what to enter into the
search field. Here, the search title from before is reused as a hint.

Next, the Android Manifest must be extended by three things: first, metadata that tells it where to
find the search interface; second, which activity should handle the search intents; and third, the
main activity must use launch mode singleTop to be able to handle the search intents itself. All
changes must be made to the activity tag for the main activity in the AndroidManifest.xml,
as shown in Listing 8.47.

Listing 8.47 Setting Up Search in the Android Manifest


<activity
android:launchMode="singleTop" // Reuses existing instance
android:name=".view.main.MainActivity"
android:label="@string/app_name">
<meta-data android:name="android.app.searchable" // Where to find searchable conf.
android:resource="@xml/searchable" />
<intent-filter>
<action android:name="android.intent.action.SEARCH" /> // Handles search intents
</intent-filter>

</activity>

The singleTop launch mode tells Android to route any intents to MainActivity to the existing
instance of the activity. Without it, Android would create a new instance of MainActivity for
each intent, causing its state to be lost. Thus, to handle its own searches itself, the activity must
use singleTop as launch mode.
Letting Users Search Foods 267

To inflate a menu into an activity, you must override the onCreateOptionsMenu method. So in
MainActivity, override this method as shown in Listing 8.48 to inflate the search menu.

Listing 8.48 Inflating and Setting Up the Search Menu


import android.app.SearchManager
import android.widget.SearchView
import android.content.Context
import android.view.Menu
// …
class MainActivity : AppCompatActivity() {
// …
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.search_menu, menu)

// Associates searchable configuration with the SearchView


val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
(menu.findItem(R.id.search).actionView as SearchView).apply {
setSearchableInfo(searchManager.getSearchableInfo(componentName))
}
return true
}
}

Finally, to handle search intents (when a user initiates a search), override the onNewIntent
method and filter for the ACTION_SEARCH intent, as demonstrated in Listing 8.49.

Listing 8.49 Handling Search Intents


import android.content.Intent
// …
class MainActivity : AppCompatActivity() {
// …
override fun onNewIntent(intent: Intent) {
if (intent.action == Intent.ACTION_SEARCH) { // Filters for search intents
val query = intent.getStringExtra(SearchManager.QUERY)
updateListFor(query)
}
}
}
268 Chapter 8 Android App Development with Kotlin: Nutrilicious

When you run the app now, you should be able to search for any food you want and get the rele-
vant results displayed in the RecyclerView. If nothing is shown, make sure to enter a query that
returns a result, such as “raw”—the app does not handle empty responses yet.

Introducing Fragments I: The Search Fragment


At this point, the Home Screen is almost finished, except for the listeners for the RecyclerView.
These will be added later and will also be used by the My Foods Screen. In order to prevent the
MainActivity from becoming a god activity that tries to handle everything itself, you will now
modularize the MainActivity into fragments.

Fragments encapsulate a part of the UI to make it reusable. Typically, an activity is made up


of multiple fragments. In this app, you will create one for each item in the bottom naviga-
tion menu, so a SearchFragment and a FavoritesFragment. In this section, you’ll modu-
larize your existing code into a SearchFragment and use it in the MainActivity, thus
making the latter a lot smaller. In the next section, it will then be easy to incorporate the
FavoritesFragment.

As always, let’s start by creating the necessary layout files. In res/layout, add a new file
fragment_search.xml that contains a SwipeRefreshLayout with the RecyclerView
from activity_main.xml as its only child—you can cut and paste the recycler view layout
from there. Remove any “constraint” attributes that refer to the ConstraintLayout
used in activity_main.xml and give it a layout_weight instead. Listing 8.50 shows
the layout.

Listing 8.50 Layout for the SearchFragment


<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipeRefresh"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v7.widget.RecyclerView
android:id="@+id/rvFoods"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>

</android.support.v4.widget.SwipeRefreshLayout>
Introducing Fragments I: The Search Fragment 269

Having cut this element from activity_main.xml, the next step is to add a placeholder for the
fragment in its place. The placeholder defines where fragments will be attached to the activity.
Listing 8.51 uses an empty FrameLayout for this.

Listing 8.51 Layout for the MainActivity


<android.support.constraint.ConstraintLayout …>

<!-- Placeholder for fragments -->


<FrameLayout
android:id="@+id/mainView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />

<android.support.design.widget.BottomNavigationView … />

</android.support.constraint.ConstraintLayout>

This wraps up the layout side, so now you can start implementing the search fragment. In view.
main, add a new file SearchFragment.kt (not using Android Studio’s wizards to create a frag-
ment). This fragment class must extend android.support.v4.app.Fragment.

Fragments have slightly different lifecycle methods you can override to initialize the UI, popu-
late dependencies, and perform other setup logic. The search fragment overrides three lifecycle
methods, which are called in the given order:
 onAttach: called when the fragment first gets attached to its context (the activity)
 onCreateView: called after onAttach (and onCreate) to initialize the UI
 onViewCreated: called directly after onCreateView returns

The search fragment uses onAttach to get a reference to its SearchViewModel, onCreateView
is used to inflate the layout, and in onViewCreated, all views are ready to be initialized.
Listing 8.52 shows the SearchFragment class this far.

Listing 8.52 Overriding Lifecycle Methods for the Search Fragment


import android.content.Context
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.*
import com.example.nutrilicious.R
270 Chapter 8 Android App Development with Kotlin: Nutrilicious

import com.example.nutrilicious.view.common.getViewModel
import com.example.nutrilicious.viewmodel.SearchViewModel

class SearchFragment : Fragment() {

private lateinit var searchViewModel: SearchViewModel

override fun onAttach(context: Context?) {


super.onAttach(context)
searchViewModel = getViewModel(SearchViewModel::class)
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,


savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_search, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {


super.onViewCreated(view, savedInstanceState)
setUpSearchRecyclerView() // Will come from MainActivity
setUpSwipeRefresh() // Implemented later
}
}

Even though the lifecycle methods are slightly different, the concepts and structure are the
same as in activities—such as using a late-initialized property for the view model. Recall that
you can use Ctrl+O (also on Mac) to override members.

The setUpSearchRecyclerView method referenced in onViewCreated already


exists but not yet in the fragment. You can now move it from MainActivity to the
SearchFragment. Also, move updateListFor to the search fragment and make it public.
After this, MainActivity should have no more private members other than the navigation
listener (it no longer needs a reference to the view model either). In MainActivity, you can
remove all references to the removed members (setUpSearchRecyclerView, updateList-
For, and searchViewModel).

This code uses a different getViewModel extension, which is defined on the Fragment class,
as shown in Listing 8.53.
Introducing Fragments I: The Search Fragment 271

Listing 8.53 getViewModel Extension for Fragments


fun <T : ViewModel> Fragment.getViewModel(modelClass: KClass<T>): T {
return ViewModelProviders.of(this).get(modelClass.java)
}

Because you are now using fragments, you should always consider the possibility that the frag-
ment is not attached to its activity so that views are inaccessible. In Kotlin, this can be handled
concisely using the safe call operator on the UI elements. Listing 8.54 adjusts updateListFor
accordingly.

Listing 8.54 Accessing UI Elements Safely


fun updateListFor(searchTerm: String) { // Is now public
launch(NETWORK) { // …
withContext(UI) {
(rvFoods?.adapter as? SearchListAdapter)?.setItems(foods) // Uses safe ops.
}
}
}

Next, adjust setUpSearchRecyclerView by replacing references to this@MainActivity with


the fragment’s context, as in Listing 8.55.

Listing 8.55 Adjusting the RecyclerView Setup


private fun setUpSearchRecyclerView() = with(rvFoods) {
adapter = SearchListAdapter(emptyList())
layoutManager = LinearLayoutManager(context)
addItemDecoration(DividerItemDecoration(
context, LinearLayoutManager.VERTICAL
))
setHasFixedSize(true)
}

There is one new layout component to set up in this fragment, the SwipeRefreshLayout.
It requires an action that reissues the last search when swiping down to refresh the data.
Listing 8.56 adds a simple setup method for it, and a lastSearch property to remember the
last search.
272 Chapter 8 Android App Development with Kotlin: Nutrilicious

Listing 8.56 Setting Up the SwipeRefreshLayout


class SearchFragment : Fragment() {
private var lastSearch = ""
// …
private fun setUpSwipeRefresh() {
swipeRefresh.setOnRefreshListener {
updateListFor(lastSearch) // Re-issues last search on swipe refresh
}
}

fun updateListFor(searchTerm: String) {


lastSearch = searchTerm // Remembers last search term
// …
}
}

At this point, there should be no more errors in onViewCreated because all methods exist.
This concludes the SearchFragment class; it was mostly a matter of moving parts of the
MainActivity to this new fragment to split responsibilities.

Naturally, the MainActivity now needs adjustments to use this fragment. First, remove any
references to the removed methods and to the SearchViewModel—the activity itself no longer
needs to access a view model or retrieve data. Instead, add a property of type SearchFragment to
hold the search fragment, as in Listing 8.57.

Listing 8.57 Adding a Property for the Fragment


class MainActivity : AppCompatActivity() {
private lateinit var searchFragment: SearchFragment
// …
}

Adding fragments to an activity is done using fragment transactions. To get started, let’s add an
extension function that encapsulates the boilerplate necessary to add a fragment to an activity.
Listing 8.58 shows this new extension from ViewExtensions.kt.

Listing 8.58 Extension Function to Include Fragments


import android.support.v7.app.AppCompatActivity
// …
fun AppCompatActivity.replaceFragment(viewGroupId: Int, fragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(viewGroupId, fragment) // Replaces given view group with fragment
.commit()
}
Introducing Fragments I: The Search Fragment 273

Any fragment transaction is initiated via the activity’s supportFragmentManager. The


given viewGroupId refers to the placeholder view that shall be replaced by the fragment.
With this, you could now add this new fragment to the activity in onCreate, as shown in
Listing 8.59.

Listing 8.59 Including a Fragment into the UI


import com.example.nutrilicious.view.common.replaceFragment

class MainActivity : AppCompatActivity() {


// …
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
searchFragment = SearchFragment()
replaceFragment(R.id.mainView, searchFragment) // Replaces the placeholder
navigation.setOnNavigationItemSelectedListener(navListener)
}
}

However, this creates a completely new SearchFragment every time onCreate is called; for
instance, when the user rotates the screen or switches to another app and back. This causes
any state in the fragment to be cleared after such actions, which evokes a feeling of discontinu-
ity for the user. Instead, you want to keep the search fragment—and its state—alive as long as
Android does not garbage-collect it. Storing a fragment is also done using fragment transactions.
Listing 8.60 shows a new extension to ViewExtensions.kt that adds a fragment to an
activity’s state.

Listing 8.60 Extension to Store Fragment in Activity’s State


import android.support.annotation.IdRes
// …
fun AppCompatActivity.addFragmentToState(
@IdRes containerViewId: Int,
fragment: Fragment,
tag: String
) {
supportFragmentManager.beginTransaction()
.add(containerViewId, fragment, tag) // Stores fragment with given tag
.commit()
}
274 Chapter 8 Android App Development with Kotlin: Nutrilicious

Now, instead of plainly creating a new fragment each time in onCreate, you can implement a
helper method that tries to recover an existing fragment first and only creates one if required.
Listing 8.61 shows the necessary code.

Listing 8.61 Restoring an Existing Fragment


import com.example.nutrilicious.view.common.*
// …
class MainActivity : AppCompatActivity() {
// …
private fun recoverOrBuildSearchFragment() {
val fragment = supportFragmentManager // Tries to load fragment from state
.findFragmentByTag(SEARCH_FRAGMENT_TAG) as? SearchFragment
if (fragment == null) setUpSearchFragment() else searchFragment = fragment
}

private fun setUpSearchFragment() { // Sets up search fragment and stores to state


searchFragment = SearchFragment()
addFragmentToState(R.id.mainView, searchFragment, SEARCH_FRAGMENT_TAG)
}
}

The recoverOrBuildSearchFragment method first tries to read an existing fragment from the
activity’s state and otherwise falls back to creating a new one. When creating a new one, the frag-
ment is automatically added to the activity’s state for next time. The tag is a unique identifier for
the fragment that you can declare above the MainActivity as in Listing 8.62.

Listing 8.62 Adding a Fragment Tag


private const val SEARCH_FRAGMENT_TAG = "SEARCH_FRAGMENT"

class MainActivity : AppCompatActivity() { … }

Next, call the new helper method in onCreate to recover the fragment instead of creating a new
instance, as shown in Listing 8.63.

Listing 8.63 Adjusting onCreate


override fun onCreate(savedInstanceState: Bundle?) {
// …
recoverOrBuildSearchFragment() // Replaces SearchFragment() constructor call
replaceFragment(R.id.mainView, searchFragment)
}
Introducing Fragments I: The Search Fragment 275

You can now delegate to the fragment to handle search intents because this is no longer the
activity’s responsibility. Listing 8.64 adjusts the intent handler accordingly.

Listing 8.64 Delegating Searches to the Search Fragment


override fun onNewIntent(intent: Intent) {
if (intent.action == Intent.ACTION_SEARCH) {
val query = intent.getStringExtra(SearchManager.QUERY)
searchFragment.updateListFor(query) // Uses the search fragment
}
}

The final enhancement in this section is to indicate progress properly using the swipe refresh
layout. Currently, the swipe refresh indicator doesn’t show up when issuing a search via the menu
and will not disappear once it has been triggered by swiping down. You can fix both issues easily
inside the updateListFor method by setting the swipe refresh state appropriately, as shown in
Listing 8.65.

Listing 8.65 Handling Swipe Refresh


private fun updateListFor(searchTerm: String) {
lastSearch = searchTerm
swipeRefresh?.isRefreshing = true // Indicates that app is loading

launch(NETWORK) {
val foods = searchViewModel.getFoodsFor(searchTerm)
withContext(UI) {
(rvFoods?.adapter as? SearchListAdapter)?.setItems(foods)
swipeRefresh?.isRefreshing = false // Indicates that app finished loading
}
}
}

Note that this also uses the safe call operator because the fragment may already be detached from
its activity by the time the network request returns, making swipeRefresh inaccessible.

Now everything should work as before (plus the swipe refresh functionality). Users do not notice
if the app uses fragments, but it does improve the internal structure. In particular, it prevents god
activities by separating concerns on a more fine-grained level.
276 Chapter 8 Android App Development with Kotlin: Nutrilicious

Introducing Fragments II: The Favorites Fragment


Now that the app uses fragments, it is time to add the favorites fragment and make the bottom
navigation work by showing the appropriate fragment. As always, the first step is to create the
required layouts. For this, add a new file fragment_favorites.xml in res/layout that defines
the layout for the new fragment. This fragment shows the list of user-selected favorite foods.
Listing 8.66 shows the corresponding layout.

Listing 8.66 Layout for the Favorites Fragment


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/tvHeadline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:padding="@dimen/medium_padding"
android:text="@string/favorites"
android:textSize="@dimen/huge_font_size" />

<android.support.v7.widget.RecyclerView
android:id="@+id/rvFavorites"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/tvHeadline" />

</android.support.constraint.ConstraintLayout>

This simple layout consists of a headline and a recycler view to show all the user’s favorite foods.
Although this uses the more modern ConstraintLayout, you could easily implement this as a
vertical LinearLayout as well. To finish the layout, you must again add the missing resources as
shown in Listing 8.67.
Introducing Fragments II: The Favorites Fragment 277

Listing 8.67 Resources for the Layout


// In res/values/dimens.xml
<dimen name="huge_font_size">22sp</dimen>

// In res/values/strings.xml
<string name="favorites">Favorite Foods</string>

That’s all the layout needed for this fragment. Now, create a new file FavoritesFragment.kt in
view.main and add the necessary overrides to inflate and initialize the layout components. As
shown in Listing 8.68, this follows the same structure as in the search fragment. For now, the frag-
ment uses hard-coded sample data because users cannot select favorites yet.

Listing 8.68 Implementing the Favorites Fragment


import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.*
import android.view.*
import com.example.nutrilicious.R
import com.example.nutrilicious.model.Food
import kotlinx.android.synthetic.main.fragment_favorites.*

class FavoritesFragment : Fragment() {

override fun onCreateView(inflater: LayoutInflater,


container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_favorites, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {


super.onViewCreated(view, savedInstanceState)
setUpRecyclerView()
}

private fun setUpRecyclerView() = with(rvFavorites) {


adapter = SearchListAdapter(sampleData())
layoutManager = LinearLayoutManager(context)
addItemDecoration(DividerItemDecoration(
context, LinearLayoutManager.VERTICAL
278 Chapter 8 Android App Development with Kotlin: Nutrilicious

))
setHasFixedSize(true)
}

// Temporary! Should use string resources instead of hard-coded strings in general


private fun sampleData(): List<Food> = listOf(
Food("00001", "Marshmallow", "Candy and Sweets", true),
Food("00002", "Nougat", "Candy and Sweets", true),
Food("00003", "Oreo", "Candy and Sweets", true)
)
}

Again, the layout is inflated in onCreateView and then all views are initialized in onView-
Created because that is when they are ready to be manipulated. This initialization works just
fine. However, note how the initialization for the RecyclerView is now duplicated—it works just
the same way in the search fragment. This is because both use the same adapter and should look
exactly the same. The only difference between the two is which items are shown. To avoid code
duplication, you should move the logic to a place where both fragments can access it. Because
this particular logic is related to the MainActivity, let’s place it there, as shown in Listing 8.69.

Listing 8.69 Moving Common Logic into MainActivity


import android.support.v7.widget.*
import com.example.nutrilicious.model.Food
// …
class MainActivity : AppCompatActivity() {
// …
companion object {
fun setUpRecyclerView(rv: RecyclerView, list: List<Food> = emptyList()) {
with(rv) {
adapter = SearchListAdapter(list)
layoutManager = LinearLayoutManager(context)
addItemDecoration(DividerItemDecoration(
context, LinearLayoutManager.VERTICAL
))
setHasFixedSize(true)
}
}
}
}
Introducing Fragments II: The Favorites Fragment 279

Placing this method in a companion object allows you to call it more conveniently, directly on
the activity class. By delegating to this method from both fragments, your code becomes DRY
again (“Don’t Repeat Yourself”). Listing 8.70 demonstrates the changes.

Listing 8.70 Removing Duplicated Code from the Fragments


// In FavoritesFragment.kt
private fun setUpRecyclerView() {
MainActivity.setUpRecyclerView(rvFavorites, sampleData())
}

// In SearchFragment.kt
private fun setUpSearchRecyclerView() {
MainActivity.setUpRecyclerView(rvFoods)
}

This way, the logic to set up the recycler view is encapsulated in one place and the fragments
delegate to it. You can now use the fragments to make the bottom navigation menu work. Thanks
to the extension that wraps fragment transactions, this is easy now. Listing 8.71 adjusts the navi-
gation listener in MainActivity accordingly.

Listing 8.71 Implementing the Bottom Navigation Menu


private val handler = BottomNavigationView.OnNavigationItemSelectedListener {
when (it.itemId) {
R.id.navigation_home -> {
replaceFragment(R.id.mainView, searchFragment) // Uses existing search fragment
return@OnNavigationItemSelectedListener true
}
R.id.navigation_my_foods -> {
replaceFragment(R.id.mainView, FavoritesFragment()) // Creates new fragment
return@OnNavigationItemSelectedListener true
}
}
false
}

When clicking to the Home Screen, the activity switches to the existing search fragment that it
keeps in the searchFragment property. For the favorites fragment, this is not necessary. Its
state will be based on which foods are stored in the database as favorites. Thus, a new fragment is
created whenever the user navigates to the My Foods Screen.
280 Chapter 8 Android App Development with Kotlin: Nutrilicious

Currently, the search fragment will be empty after switching to the favorites fragment and back.
So as a final step, the search fragment should remember the most recent search results and use
them to populate the list when returning to the search fragment. Listing 8.72 shows the required
changes in SearchFragment.

Listing 8.72 Retaining the Last Search Results


import com.example.nutrilicious.model.Food

class SearchFragment : Fragment() {


// …
private var lastResults = emptyList<Food>()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {


// …
(rvFoods?.adapter as? SearchListAdapter)?.setItems(lastResults) // Recovers state
}
// …
fun updateListFor(searchTerm: String) {
// …
launch(NETWORK) {
val foods = searchViewModel.getFoodsFor(searchTerm)
lastResults = foods // Remembers last search results
withContext(UI) { … }
}
}
}

Store User’s Favorite Foods in a Room Database


The next step is to store a user’s favorite foods in a database and show those in the favorites frag-
ment. This gives you another chance to familiarize yourself with Room. Setting it up requires the
same three steps as in Chapter 7, Android App Development with Kotlin: Kudoo App:
 Define the entities that should be stored in the database.
 Create the DAOs that offer all desired operations to access the database.
 Implement an abstract subclass of RoomDatabase that provides the DAOs.

Because the dependencies for all Android Architecture Components are already included, you can
start creating the entities right away. For this app, that’s the Food class. Listing 8.73 shows the
required changes.
Store User’s Favorite Foods in a Room Database 281

Listing 8.73 Making Food an Entity


import android.arch.persistence.room.*
// …
@Entity(tableName = "favorites") // Signals Room to map this entity to the DB
data class Food(
@PrimaryKey val id: String, // Unique identifier for a food (the NDBNO)
val name: String,
val type: String,
var isFavorite: Boolean = false
) { … }

Only two annotations are necessary to turn the class into an entity that Room can work with.
The table name “favorites” better highlights the purpose than the default name “food.” The exist-
ing NDBNO is used as the primary key.

Next step is to add the DAO to perform queries on the favorites table. To this end, add a new
package data.db and add a FavoritesDao interface to it, as implemented in Listing 8.74.

Listing 8.74 Adding the DAO to Access Favorites


import android.arch.lifecycle.LiveData
import android.arch.persistence.room.*
import android.arch.persistence.room.OnConflictStrategy.IGNORE
import com.example.nutrilicious.model.Food

@Dao
interface FavoritesDao {

@Query("SELECT * FROM favorites")


fun loadAll(): LiveData<List<Food>> // Note LiveData return type

@Query("SELECT id FROM favorites")


fun loadAllIds(): List<String>

@Insert(onConflict = IGNORE) // Do nothing if food with same NDBNO already exists


fun insert(food: Food)

@Delete
fun delete(food: Food)
}
282 Chapter 8 Android App Development with Kotlin: Nutrilicious

The DAO offers all database-related capabilities the Nutrilicious app needs: fetching all favorites
(to display them in the fragment), fetching only the IDs of all favorite foods (to highlight them
with a star), and adding and deleting favorites (for when the user clicks on a star). Room makes it
straightforward to implement the corresponding queries using basic SQL queries. Also note that
fetchAll makes use of LiveData so that updates to favorites can be reflected immediately in the
favorites fragment.

The third and last step is to add an abstract subclass of RoomDatabase, which here is named
AppDatabase. This always follows the structure so Listing 8.75 simply shows its definition.

Listing 8.75 Adding the AppDatabase


import android.arch.persistence.room.*
import android.content.Context
import com.example.nutrilicious.model.Food

@Database(entities = [Food::class], version = 1)


abstract class AppDatabase : RoomDatabase() {

companion object {
private var INSTANCE: AppDatabase? = null

fun getInstance(ctx: Context): AppDatabase {


if (INSTANCE == null) { INSTANCE = buildDatabase(ctx) }
return INSTANCE!!
}

private fun buildDatabase(ctx: Context) = Room


.databaseBuilder(ctx, AppDatabase::class.java, "AppDatabase")
.build()
}

abstract fun favoritesDao(): FavoritesDao // Provides access to the DAO


}

You can use this as a template. All that needs adjustment for other databases is which entities to
include in the @Database annotation, and which DAOs to expose. Here, the only DAO is exposed
via the favoritesDao method so that Room generates an implementation for it.

In order to decouple the fragments and activities that use data from the database, you should add
a FavoritesViewModel to the viewmodel package. Listing 8.76 shows this view model.
Store User’s Favorite Foods in a Room Database 283

Listing 8.76 The View Model to Access Favorite Foods


import android.app.Application
import android.arch.lifecycle.*
import com.example.nutrilicious.data.db.*
import com.example.nutrilicious.model.Food
import kotlinx.coroutines.*

class FavoritesViewModel(app: Application) : AndroidViewModel(app) {

private val dao by lazy { AppDatabase.getInstance(getApplication()).favoritesDao()}

suspend fun getFavorites(): LiveData<List<Food>> = withContext(DB) {


dao.loadAll()
}
suspend fun getAllIds(): List<String> = withContext(DB) { dao.loadAllIds() }
fun add(favorite: Food) = launch(DB) { dao.insert(favorite) }
fun delete(favorite: Food) = launch(DB) { dao.delete(favorite) }
}

The view model wraps the DAO’s methods and provides a clean interface for them. Retrieving
all the user’s favorite foods returns a LiveData object so that you can observe changes. Both
methods with return value use withContext in order to use the natural return type instead of a
Deferred, whereas adding and deleting elements is taken care of in dedicated “fire-and-forget”
coroutines. Note that this view model is a subclass of AndroidViewModel because it needs a
reference to the application context to retrieve the database object.

This implementation uses a dedicated dispatcher for all database-related actions. It is declared in
DatabaseDispatcher.kt inside the data.db package, as in Listing 8.77.

Listing 8.77 Coroutine Dispatcher for Database Operations


import kotlinx.coroutines.newSingleThreadContext

val DB = newSingleThreadContext("DB") // Single dedicated thread for DB operations

This concludes the database setup. The next step is to make use of it when the user clicks on a star
to mark or unmark it as a favorite. In other words, the ImageView needs a click handler. This click
handler should be passed into the adapter, and it should know which Food object and which list
position a click refers to. Listing 8.78 adjusts the SearchListAdapter accordingly.
284 Chapter 8 Android App Development with Kotlin: Nutrilicious

Listing 8.78 Adjusting the Adapter to Handle Star Icon Clicks


class SearchListAdapter( // …
private val onStarClick: (Food, Int) -> Unit
) : … {
// …
inner class ViewHolder(…) : … {
fun bindTo(food: Food) {
// …
ivStar.setOnClickListener { onStarClick(food, this.layoutPosition) }
}
}
}

Only two additional lines of code are necessary to pass in and assign the click handler. You now
have to pass in a handler when creating the adapter in MainActivity. That handler should
toggle a food as being a favorite. Since this will be possible from both fragments, the toggling
logic should be placed in MainActivity (or in a separate file) to avoid code duplication.

First, adjust the RecyclerView setup as shown in Listing 8.79 to construct the adapter. Also, to
avoid a large companion object with lots of effectively static methods, I’d suggest removing the
companion object at this point. You will adjust their calls in the fragments later.

Listing 8.79 Creating the Adapter with Click Listener


class MainActivity : AppCompatActivity() {
// …
fun setUpRecyclerView(rv: RecyclerView, list: List<Food> = emptyList()) {
with(rv) {
adapter = setUpSearchListAdapter(rv, list)
// …
}
}

private fun setUpSearchListAdapter(rv: RecyclerView, items: List<Food>) =


SearchListAdapter(items,
onStarClick = { food, layoutPosition -> // Toggles favorite on click
toggleFavorite(food)
rv.adapter.notifyItemChanged(layoutPosition)
})
}
Store User’s Favorite Foods in a Room Database 285

The adapter creation is now encapsulated into its own method because it has become a little more
complex. The action for onStarClick toggles the food that corresponds to the click as a favorite
and notifies the adapter of the change to that particular item. This causes that single item to be
redrawn so that the star icon updates correctly—thus the need for the layout position in the click
handler.

Now it’s time to implement the method that toggles a favorite. For this, the MainActivity needs
a reference to the FavoritesViewModel in order to add or delete favorites. Listing 8.80 presents
the required changes.

Listing 8.80 Toggling Favorites on Star Icon Click


import com.example.nutrilicious.viewmodel.FavoritesViewModel

class MainActivity : AppCompatActivity() {


// …
private lateinit var favoritesViewModel: FavoritesViewModel

override fun onCreate(savedInstanceState: Bundle?) {


// …
favoritesViewModel = getViewModel(FavoritesViewModel::class)
}
// …
private fun toggleFavorite(food: Food) {
val wasFavoriteBefore = food.isFavorite
food.isFavorite = food.isFavorite.not() // Adjusts Food object’s favorite status

if (wasFavoriteBefore) {
favoritesViewModel.delete(food)
toast("Removed ${food.name} from your favorites.")
} else {
favoritesViewModel.add(food)
toast("Added ${food.name} as a new favorite of yours!")
}
}
}

Toggling a food as favorite changes the corresponding property in the Food object and also trig-
gers an insert or delete in the database. This automatically makes them appear or disappear in the
favorites fragment due to the use of LiveData. Toast messages give the user feedback on his or her
286 Chapter 8 Android App Development with Kotlin: Nutrilicious

action. Note that they may appear before the database operation is actually completed (because
add and delete use launch internally), but this will be unnoticeable for users. As you may
expect, creating toasts this way is enabled by an extension function in ViewExtensions.kt, the
one shown in Listing 8.81.

Listing 8.81 Extension Function to Create Toasts


import android.widget.Toast
// …
fun AppCompatActivity.toast(msg: String) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
}

This concludes the changes to the activity. What is left are the changes to the two fragments: The
favorites fragment should display all favorites, and the search fragment should indicate which of
the found foods are already favorites.

First, let’s adjust the FavoritesFragment. It should now use the FavoritesViewModel to
retrieve all favorites and observe changes to the LiveData to update the RecyclerView. So at this
point, the sample data can be removed as well. Listing 8.82 shows the changes.

Listing 8.82 Favorites Fragment with View Model


import android.content.Context
import com.example.nutrilicious.view.common.getViewModel
import com.example.nutrilicious.viewmodel.FavoritesViewModel
import kotlinx.coroutines.android.UI
import kotlinx.coroutines.launch
import android.arch.lifecycle.Observer
// …
class FavoritesFragment : Fragment() {

private lateinit var favoritesViewModel: FavoritesViewModel

override fun onAttach(context: Context?) {


super.onAttach(context)
favoritesViewModel = getViewModel(FavoritesViewModel::class)
}
// …
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// …
Store User’s Favorite Foods in a Room Database 287

observeFavorites()
}

private fun observeFavorites() = launch { // Updates list when favorites change


val favorites = favoritesViewModel.getFavorites()
favorites.observe(this@FavoritesFragment, Observer { foods ->
foods?.let {
launch(UI) { (rvFavorites.adapter as? SearchListAdapter)?.setItems(foods) }
}
})
}

private fun setUpRecyclerView() {


(activity as? MainActivity)?.setUpRecyclerView(rvFavorites, emptyList())
}
}

This fragment gets a new property to hold the LiveData of foods. It can be initialized already
in onAttach because it is independent of any views. The observeFavorites method reflects
changes on the LiveData in the RecyclerView. The setUpRecyclerView method is now
called by accessing the fragment’s activity property to get a reference to the MainActivity
because it’s no longer a companion object method, and it passes in an empty list to
start with.

The favorites fragment now displays the correct list of favorite foods and the user is able to
choose which foods those are. However, there’s one more step: indicating favorites in the search
fragment. It gets its data from the API, so you have to use the NDBNO to connect it to the
corresponding entry in the database, as shown in Listing 8.83.

Listing 8.83 Search Fragment


import com.example.nutrilicious.viewmodel.FavoritesViewModel
// …
class SearchFragment : Fragment() {
// …
private lateinit var favoritesViewModel: FavoritesViewModel

override fun onAttach(context: Context?) {


// …
favoritesViewModel = getViewModel(FavoritesViewModel::class)
}
288 Chapter 8 Android App Development with Kotlin: Nutrilicious

// …
private fun updateListFor(searchTerm: String) {
lastSearch = searchTerm
swipeRefresh?.isRefreshing = true

launch {
val favoritesIds: List<String> = favoritesViewModel.getAllIds()
val foods: List<Food> = searchViewModel.getFoodsFor(searchTerm)
.onEach { if (favoritesIds.contains(it.id)) it.isFavorite = true }
lastResults = foods

withContext(UI) { … }
}
}

private fun setUpSearchRecyclerView() {


(activity as? MainActivity)?.setUpRecyclerView(rvFoods)
}
}

The view model takes care of retrieving the IDs of all favorites, allowing you to augment the data
retrieved from the API with the information regarding whether it is a favorite. Like forEach,
onEach performs the given action on each item. But in contrast to forEach, it returns the
resulting collection, here the list of foods with their adjusted isFavorite property. Note
that the setup method for the recycler view is adjusted to call the member method
correctly.

With this, users are able to choose their favorite foods, view them in the My Foods Screen, see
which found foods already are favorites, and add or remove favorites by clicking on the star
icon. This is a working app but not yet particularly helpful, except to discover foods users
were not previously aware existed. In the following section, you dive deeper into the USDA
API to retrieve and show detailed nutritional information and help users make healthier
diet choices.

Fetching Detailed Nutrition Data from the USDA Food


Reports API
Accessing another endpoint of the USDA API is fairly easy at this point because you can build
upon the existing code. To fetch nutrition details from the USDA Food Reports API,9 you just have
to add the corresponding GET request to the UsdaApi interface, as shown in Listing 8.84.

9. https://ndb.nal.usda.gov/ndb/doc/apilist/API-FOOD-REPORTV2.md
Fetching Detailed Nutrition Data from the USDA Food Reports API 289

Listing 8.84 Adding a New Endpoint to the API Interface


@GET("V2/reports?format=json")
fun getDetails(
@Query("ndbno") id: String, // Only non-optional parameter is food ID
@Query("type") detailsType: Char = 'b' // b = basic, f = full, s = stats
): Call<DetailsWrapper<DetailsDto>>

With this, the UsdaApi interface now allows two different GET requests. This second one appends
“V2/reports” to the base URL and accepts the NDBNO of the requested food. You could again
retrieve the raw JSON data first. But this time, let’s map the data to DTOs directly. The structure is
similar to the Search API, with a DetailsWrapper enclosing the actual DetailsDto to navigate
down the JSON structure. These wrappers and DTOs will be created next. Setting the result type to
“b” tells the API to return basic info, which is already more than detailed enough for this app.

I’d recommend adding another JSON file with the result format10 to the project (for instance, in
the sampledata directory) to easily explore it. You can call it detailsFormat.json and use
the Reformat Code action to fix line breaks and indentation if necessary. Listing 8.85 portrays a
relevant chunk of the JSON data.

Listing 8.85 JSON Format for Nutrition Details


{
"foods": [
{
"food": {
"sr": "Legacy",
"type": "b",
"desc": { // Basic food data
"ndbno": "09070",
"name": "Cherries, sweet, raw",
"ds": "Standard Reference",
"manu": "",
"ru": "g"
},
"nutrients": [ // Nutrient data for food above
{
"nutrient_id": "255", // Unique nutrient ID
"name": "Water", // Nutrient name
"derivation": "NONE",

10. https://api.nal.usda.gov/ndb/V2/reports?ndbno=09070&type=b&format=json&api_key=DEMO_KEY
290 Chapter 8 Android App Development with Kotlin: Nutrilicious

"group": "Proximates", // Nutrient category


"unit": "g", // Unit used by ‘value’ below
"value": "82.25", // Amount of this nutrient per 100g of food
"measures": [ … ]
}, …
]
}
}
]
}

This time, the actual data you need is wrapped into the properties foods and food (just like list
and item before). The list of nutrients is long and contains everything from water to macros to
vitamins, minerals, and fats. All values refer to 100 grams of the food. For instance, you can see
from Listing 8.85 that 100g of cherries contain 82.25g of water. In this app, you will not use the
alternative measures that come with the result (such as oz, cups, or pieces).

Let’s map this data to DTOs. In data.network.dto, add a new file DetailsDtos.kt into
which you can place your DTOs. Listing 8.86 provides the DTO wrappers that navigate to the
interesting data.

Listing 8.86 DTO Wrappers


import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
class FoodsWrapper<T> {
var foods: List<T> = listOf() // Navigates down the ‘foods’ list from JSON
}

@JsonClass(generateAdapter = true)
class FoodWrapper<T> {
var food: T? = null // Navigates down the ‘food’ object
}

typealias DetailsWrapper<T> = FoodsWrapper<FoodWrapper<T>>

When writing these DTOs, be careful about which JSON property holds a list (indicated by square
brackets) and which holds an object (indicated by curly braces), and map them accordingly. In
this example, foods is a list and food is an object; the Food Reports API allows fetching details for
multiple foods in one request, thus the list of foods.
Fetching Detailed Nutrition Data from the USDA Food Reports API 291

Next, Listing 8.87 provides the DTO declarations with property names matching those in the
JSON data, so that Moshi knows how to map them.

Listing 8.87 Details DTOs


@JsonClass(generateAdapter = true)
class DetailsDto(val desc: DescriptionDto, val nutrients: List<NutrientDto>) {
init {
nutrients.forEach { it.detailsId = desc.ndbno } // Connects the two DTOs below
}
}

@JsonClass(generateAdapter = true)
class DescriptionDto { // Property names must match JSON names
lateinit var ndbno: String
lateinit var name: String
}

@JsonClass(generateAdapter = true)
class NutrientDto { // Property names must match JSON names
var nutrient_id: Int? = null // Cannot use lateinit with Int
var detailsId: String? = null // Only field not coming from JSON
lateinit var name: String
lateinit var unit: String
var value: Float = 0f
lateinit var group: String
}

The response contains a description with the NDBNO and name of the requested food,
followed by the list of nutrient details. The NutrientDto contains one property that is
not populated from the JSON data, namely the detailsId. This is used to reference which
food (identified by the NDBNO) the nutrient details belong to. It is initialized in the
DetailsDto when merging the description and nutrient data. This works similar to a
foreign key in SQL.

You are now all set to retrieve and map detailed nutrition data from the USDA API. Finally, you
can map this to domain classes that are decoupled from the JSON property names and formats.
So in model, add a new file FoodDetails.kt that will contain data classes for the food details.
Listing 8.88 presents the required data classes.
292 Chapter 8 Android App Development with Kotlin: Nutrilicious

Listing 8.88 Domain Classes for Food Details


import com.example.nutrilicious.data.network.dto.*

data class FoodDetails(


val id: String,
val name: String,
val nutrients: List<Nutrient>
) {
constructor(dto: DetailsDto) : this(
dto.desc.ndbno,
dto.desc.name,
dto.nutrients.map(::Nutrient)
)
}

data class Nutrient(


val id: Int,
val detailsId: String,
val name: String,
val amountPer100g: Float,
val unit: String,
val type: NutrientType
) {
constructor(dto: NutrientDto) : this(
dto.nutrient_id!!,
dto.detailsId!!,
dto.name,
dto.value,
dto.unit,
NutrientType.valueOf(dto.group.toUpperCase())
)
}

enum class NutrientType {


PROXIMATES, MINERALS, VITAMINS, LIPIDS, OTHER
}
Integrating the Details Activity 293

There are three domain classes. The entry point is the FoodDetails class that contains a
list of nutrients, each of which has a specific nutrient type. Like last time, secondary construc-
tors map the DTOs to these domain classes. In case a late-initialized property was not popu-
lated by Moshi or a nullable property remains null, the code crashes immediately when
trying to map at runtime (and you would be able to identify the causing property easily).
As for the nutrient type, there are only five possible values returned by the API, so you can
map this to an enum.

This concludes accessing the Food Reports API. You can sanity-check it in MainActivity.onCreate
with a temporary test call as shown in Listing 8.89.

Listing 8.89 Making a Test Request


import com.example.nutrilicious.data.network.*
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {


override fun onCreate(savedInstanceState: Bundle?) {
// …
launch(NETWORK) { usdaApi.getDetails("09070").execute() } // Temporary
}
}

When running the app, you should see a response with the details in JSON format in the Logcat.
Now you are ready to implement a detail activity that presents the user with more actionable
information about each food in the app.

Integrating the Details Activity


In this section, you will create a second activity to display the nutritional information about a
selected food. To this end, create a new package view.details, and inside it generate a new
Empty Activity along with its layout file using Android Studio’s wizard (using right-click, New,
Activity, and then Empty Activity). Name it DetailsActivity and use the default name
activity_details.xml for the layout.

As always, let’s deal first with the layout. This layout file is quite long because it has four sections
for macronutrients (“proximates” in the API response), vitamins, minerals, and lipids (fats).
Each section will have a headline and a text view that is populated with data programmatically,
followed by a horizontal divider. This divider is a custom view defined in res/layout/
horizontal_divider.xml as shown in Listing 8.90.
294 Chapter 8 Android App Development with Kotlin: Nutrilicious

Listing 8.90 Horizontal Divider Layout


<?xml version="1.0" encoding="utf-8"?>
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/divider_height"
android:minHeight="@dimen/divider_minheight"
android:layout_marginTop="@dimen/divider_margin"
android:layout_marginBottom="@dimen/divider_margin"
android:background="?android:attr/listDivider" />

This renders as a horizontal line that is 2dp thick but at least 1px to avoid being invisible. For the
dimensions, add the missing resources from Listing 8.91 to res/values/dimens.xml.

Listing 8.91 Divider Dimensions


<dimen name="divider_height">2dp</dimen>
<dimen name="divider_minheight">1px</dimen>
<dimen name="divider_margin">5dp</dimen>

With this, you are ready to implement the details activity layout, as shown in Listing 8.92. This
code belongs in res/layout/activity_details.xml.

Listing 8.92 Details Activity Layout


<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/medium_padding"
tools:context=".view.detail.FoodDetailsActivity">

<TextView
android:id="@+id/tvFoodName"
android:layout_width="match_parent"
Integrating the Details Activity 295

android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="@dimen/medium_padding"
android:textSize="@dimen/huge_font_size" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/proximates"
android:textColor="@android:color/darker_gray"
android:textSize="@dimen/medium_font_size" />

<TextView
android:id="@+id/tvProximates"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lineSpacingMultiplier="1.1" />

<include layout="@layout/horizontal_divider" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/vitamins"
android:textColor="@android:color/darker_gray"
android:textSize="@dimen/medium_font_size" />

<TextView
android:id="@+id/tvVitamins"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

<include layout="@layout/horizontal_divider" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/minerals"
296 Chapter 8 Android App Development with Kotlin: Nutrilicious

android:textColor="@android:color/darker_gray"
android:textSize="@dimen/medium_font_size" />

<TextView
android:id="@+id/tvMinerals"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

<include layout="@layout/horizontal_divider" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lipids"
android:textColor="@android:color/darker_gray"
android:textSize="@dimen/medium_font_size" />

<TextView
android:id="@+id/tvLipids"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

</LinearLayout>

</ScrollView>

It is a simple ScrollView containing a vertical LinearLayout with all the sections stacked up.
There is nothing particular to note about this layout; however, to make it work, you have to add
the missing string resources for the headline, shown in Listing 8.93.

Listing 8.93 String Resources For Headlines


<string name="proximates">Proximates</string>
<string name="minerals">Minerals</string>
<string name="vitamins">Vitamins</string>
<string name="lipids">Lipids</string>

At this point, the new activity is already usable, although it will only show static texts and no
nutrition data yet. To incorporate it into the app, it should be shown when the user clicks on a
food item in a RecyclerView. Handling these clicks requires a small change in the adapter, as
shown in Listing 8.94.
Integrating the Details Activity 297

Listing 8.94 Adding an Item Click Listener to the Adapter


class SearchListAdapter(…,
private val onItemClick: (Food) -> Unit,
private val onStarClick: (Food, Int) -> Unit
) : RecyclerView.Adapter<ViewHolder>() {
// …

inner class ViewHolder(…) : … {

fun bindTo(food: Food) {


// …
containerView.setOnClickListener { onItemClick(food) }
}
}
}

The containerView property corresponds to the whole list item, so it should be the target for
this click handler. The handler accepts the food that corresponds to the clicked item in order
to pass on the NDBNO to the DetailsActivity and show the correct data. This behavior is
defined in MainActivity, as shown in Listing 8.95.

Listing 8.95 Defining the Click Handler for List Items


import com.example.nutrilicious.view.details.DetailsActivity
// …
class MainActivity : AppCompatActivity() {
private fun setUpSearchListAdapter(rv: RecyclerView, items: List<Food>) =
SearchListAdapter(items,
onItemClick = { startDetailsActivity(it) },
onStarClick = { … }
)

private fun startDetailsActivity(food: Food) {


val intent = Intent(this, DetailsActivity::class.java).apply {
putExtra(FOOD_ID_EXTRA, food.id) // Stores the desired food’s ID in the Intent
}
startActivity(intent) // Switches to DetailsActivity
}
}
298 Chapter 8 Android App Development with Kotlin: Nutrilicious

Note the use of apply to initialize the Intent and add an extra that carries the NDBNO
of the food to display. The FOOD_ID_EXTRA identifier is declared as a file-level property in
DetailsActivity.kt (see Listing 8.96).

Listing 8.96 Identifier for the Intent Extra


const val FOOD_ID_EXTRA = "NDBNO"

class DetailsActivity : AppCompatActivity() { … }

You can now click on any list item in the app to be taken to the details activity. However, it
shows only static headlines at this stage. So the next step is to actually retrieve the desired
data. Again, the activity should get its data from a view model. To this end, add a new file
DetailsViewModel.kt to the viewmodel package, as in Listing 8.97.

Listing 8.97 View Model for Details


import android.arch.lifecycle.ViewModel
import com.example.nutrilicious.data.network.*
import com.example.nutrilicious.data.network.dto.*
import com.example.nutrilicious.model.FoodDetails
import kotlinx.coroutines.withContext
import retrofit2.Call

class DetailsViewModel : ViewModel() {

suspend fun getDetails(foodId: String): FoodDetails? {


val request: Call<DetailsWrapper<DetailsDto>> = usdaApi.getDetails(foodId)

val detailsDto: DetailsDto = withContext(NETWORK) {


request.execute().body()?.foods?.get(0)?.food // Runs request and extracts data
} ?: return null

return FoodDetails(detailsDto)
}
}

This view model defines only one method, which retrieves the details for a given food and
provides a cleaner interface for the network call by wrapping the usdaApi object. Executing
the Retrofit call works as in SearchViewModel, this time accessing only the first item of the
Integrating the Details Activity 299

foods list (there is only one because you pass a single NDBNO to the API) and its food property.
If this ends up being null or throwing an exception, the withContext block passes this on.
Consequently, the whole method returns null because of the elvis operator after withContext.
In the success case, the DTO is mapped to a FoodDetails object that is returned.

Next, remove the test call in MainActivity.onCreate. The API is now used by the view model
as it should be. In the DetailsActivity, add a property for the view model and initialize it in
onCreate as shown in Listing 8.98.

Listing 8.98 Adding the View Model to the DetailsActivity


import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import com.example.nutrilicious.R
import com.example.nutrilicious.view.common.getViewModel
import com.example.nutrilicious.viewmodel.DetailsViewModel

class DetailsActivity : AppCompatActivity() {

private lateinit var detailsViewModel: DetailsViewModel

override fun onCreate(savedInstanceState: Bundle?) {


// …
detailsViewModel = getViewModel(DetailsViewModel::class)
}
}

Finally, you can read the desired NDBNO from the intent extra (which was attached by the item
click handler), fetch the data for it, and show it to the user. Listing 8.99 reads the NDBNO and
performs the request as the first step.

Listing 8.99 Using the View Model in the Details Activity


import kotlinx.coroutines.android.UI
import kotlinx.coroutines.*
// …
class DetailsActivity : AppCompatActivity() {
// …
override fun onCreate(savedInstanceState: Bundle?) {
// …
val foodId = intent.getStringExtra(FOOD_ID_EXTRA) // Reads out desired food’s ID
300 Chapter 8 Android App Development with Kotlin: Nutrilicious

updateUiWith(foodId)
}

private fun updateUiWith(foodId: String) {


if (foodId.isBlank()) return

launch {
val details = detailsViewModel.getDetails(foodId) // Retrieves details
withContext(UI) { bindUi(details) } // Populates UI
}
}
}

After reading the intent extra, a helper function handles the network request and updates the
UI. Here, launch does not need an explicit dispatcher because getDetails dispatches to the
network context internally. Note that, as a suspending function, getDetails behaves synchro-
nously by default so that the details variable is ready to be used in the line after its declaration.
Listing 8.100 shows the code to bind the data to the views.

Listing 8.100 Displaying the Data


import com.example.nutrilicious.model.*
import kotlinx.android.synthetic.main.activity_details.*
// …
class DetailsActivity : AppCompatActivity() {
// …
private fun bindUi(details: FoodDetails?) {
if (details != null) {
tvFoodName.text = "${details.name} (100g)"
tvProximates.text = makeSection(details, NutrientType.PROXIMATES)
tvMinerals.text = makeSection(details, NutrientType.MINERALS)
tvVitamins.text = makeSection(details, NutrientType.VITAMINS)
tvLipids.text = makeSection(details, NutrientType.LIPIDS)
} else {
tvFoodName.text = getString(R.string.no_data)
}
}

private fun makeSection(details: FoodDetails, forType: NutrientType) =


details.nutrients.filter { it.type == forType }
Integrating the Details Activity 301

.joinToString(separator = "\n", transform = ::renderNutrient)

private fun renderNutrient(nutrient: Nutrient): String = with(nutrient) {


val displayName = name.substringBefore(",") // = whole name if it has no comma
"$displayName: $amountPer100g$unit"
}
}

Displaying the data is just a matter of reading out each nutrient and showing it in the correspond-
ing text view. To avoid code duplication, makeSection filters to the nutrients of a specific type
and shows each in a new line, with its name and amount in 100g of the food. Displaying each
single nutrient is handled by renderNutrient. If you want to, you can extend the app to include
the nutrient type “other” as well.

Add the missing string resource in res/values/strings.xml to display “No data available” in
case something goes wrong and the API call returns null (see Listing 8.101).

Listing 8.101 String Resource Indicating No Data


<string name="no_data">No data available</string>

As a final step, you can allow users to navigate up by making the DetailsActivity a child of the
MainActivity. Listing 8.102 adjusts the AndroidManifest.xml accordingly.

Listing 8.102 Providing Up Navigation


<application …>

<activity android:name=".view.details.DetailsActivity"
android:parentActivityName=".view.main.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".view.main.MainActivity" />
</activity>
</application>

With this, the Nutrilicious app is already a lot more actionable for users. They can explore foods
and see how much of each nutrient it contains, for instance, to see if their favorite foods have a
good nutrient profile.
302 Chapter 8 Android App Development with Kotlin: Nutrilicious

Storing Food Details in the Database


In the following, you’ll cache food details in the database when first retrieved. This avoids
unnecessary network requests when the user frequently accesses the same foods, such as his
or her favorites. To arbitrate between the database and the API as alternative data sources for
food details, a repository will be introduced, as described in “Guide to App Architecture” on the
Android Developers website.11

Because Room is already included in the project, you can start creating the entities immediately.
In this case, this means turning the FoodDetails class into an entity. In Listing 8.103, the
required annotations are added to the data classes in FoodDetails.kt.

Listing 8.103 Defining the Entities


import android.arch.persistence.room.*
// …
@Entity(tableName = "details")
@TypeConverters(NutrientListConverter::class) // Is implemented next
data class FoodDetails(
@PrimaryKey val id: String,
// …
) { constructor(dto: DetailsDto) : this(…) }

@TypeConverters(NutrientTypeConverter::class)
data class Nutrient(…) { … }

Because the nutrients don’t need to be queried on their own, they can be inlined into the
details table using type converters to keep the schema simple. Thus, only the FoodDetails
class gets an @Entity annotation and a primary key. To inline the List<Nutrient> it carries,
it uses a NutrientListConverter that defines how to map the list to a string and back.
Similarly, the Nutrient class uses a type converter to store its NutrientType as a string and
restore it back.

These two converters must be implemented manually. For this, create a file Converters.kt
under data.db. First, the NutrientListConverter uses Moshi to encode the nutrient
list as a JSON string and decode it when reading from the database. This is done in
Listing 8.104.

Listing 8.104 Type Converter for List<Nutrient>


import android.arch.persistence.room.TypeConverter
import com.example.nutrilicious.model.*
import com.squareup.moshi.*

11. https://developer.android.com/topic/libraries/architecture/guide.html
Storing Food Details in the Database 303

class NutrientListConverter {
private val moshi = Moshi.Builder().build()
private val nutrientList = Types.newParameterizedType( // Represents List<Nutrient>
List::class.java, Nutrient::class.java
)
private val adapter = moshi.adapter<List<Nutrient>>(nutrientList) // Builds adapter

@TypeConverter
fun toString(nutrient: List<Nutrient>): String = adapter.toJson(nutrient)

@TypeConverter fun toListOfNutrient(json: String): List<Nutrient>


= adapter.fromJson(json) ?: emptyList()
}

The main work is to set up a Moshi adapter that knows how to transform a List<Nutrient> into
a string. Setting it up is a little verbose and could be encapsulated, but this is only used once in
this app so we’ll go with it. To tell Room which methods it should use to map types to and from
the database, you must annotate them with @TypeConverter. This way, if Room encounters a
List<Nutrient>, it knows it can use NutrientListConverter.toString.

The second converter is simpler because it only needs the Kotlin-generated enum members, as
shown in Listing 8.105. This is also placed into Converters.kt.

Listing 8.105 Type Converter for NutrientType


class NutrientTypeConverter {

@TypeConverter
fun toString(nutrientType: NutrientType) = nutrientType.name // Type -> String

@TypeConverter
fun toNutrientType(name: String) = NutrientType.valueOf(name) // String -> Type
}

This enables Room to use these two type converters. Note that, in FoodDetails.kt, the
classes use the @TypeConverters (with an “s” at the end) to denote which converters to use,
whereas the converters themselves use @TypeConverter to tell Room that it’s a converter
method.

The next step is to add the DAO. So in data.db, add a new DetailsDao.kt as in Listing 8.106.
304 Chapter 8 Android App Development with Kotlin: Nutrilicious

Listing 8.106 DAO for Food Details


import android.arch.persistence.room.*
import com.example.nutrilicious.model.FoodDetails

@Dao
interface DetailsDao {

@Query("SELECT * FROM details WHERE id = :ndbno")


fun loadById(ndbno: String): FoodDetails?

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(food: FoodDetails)
}

Regarding this details entity, the app must be able to insert new data (to cache it in the database)
and retrieve the data for a specific food to display it in the DetailsActivity.

The last step is different from before. There is already an AppDatabase, so you can extend that
with the new entity (because both entities should be stored in the same database). Listing 8.107
includes the new entity and exposes the DetailsDao.

Listing 8.107 Extending the AppDatabase


import com.example.nutrilicious.model.FoodDetails
// …
@Database(entities = [Food::class, FoodDetails::class], version = 2) // Version 2
abstract class AppDatabase : RoomDatabase() {
// …
abstract fun favoritesDao(): FavoritesDao
abstract fun detailsDao(): DetailsDao // Now exposes a DetailsDao as well
}

The first two changes are inside the @Database annotation. First, the FoodDetails class is
added to the array of entities to include in the database. Second, the version is increased by one
because the schema has changed (there is a new entity). Lastly, there is a new method to get an
implementation of the new DAO.

If you run the app now, it will clash with the existing database on your device. During develop-
ment, you can use destructive migration so that Room simply deletes the old database. This will
cause any stored favorites to disappear, but nothing more. Listing 8.108 adjusts buildDatabase
to enable destructive migration in debug builds (during development).
Storing Food Details in the Database 305

Listing 8.108 Enabling Destructive Database Migration in Development


import com.example.nutrilicious.BuildConfig
// …
private fun buildDatabase(ctx: Context) = Room
.databaseBuilder(ctx, AppDatabase::class.java, "AppDatabase")
.apply { if (BuildConfig.DEBUG) fallbackToDestructiveMigration() }
.build()

The BuildConfig.DEBUG property is true when working in Android Studio or exporting an


unsigned APK to test on your device but not when you finally publish a signed APK. Alternately,
you could remove the old database manually from your AVD by using the Device File Explorer to
remove the directory data/data/<YOUR_PACKAGE_NAME>/databases.

The database is now extended to store food details in addition to favorite foods. For food details,
there are now two competing data sources: the USDA API and the database. If available, the
database should be used because this avoids network requests, improves performance, and works
offline. To enforce this behavior, you introduce a repository that all consumers should use to access
details data—it is the single source of truth for this data.

For the repository, add a new class DetailsRepository to the data package. This repository
offers a clean interface for all operations related to FoodDetails and uses both the network and
the database. Listing 8.109 shows its implementation.

Listing 8.109 Details Repository as Single Source of Truth


import android.content.Context
import com.example.nutrilicious.data.db.*
import com.example.nutrilicious.data.network.*
import com.example.nutrilicious.data.network.dto.*
import com.example.nutrilicious.model.FoodDetails
import kotlinx.coroutines.*
import retrofit2.Call

class DetailsRepository(ctx: Context) {

private val detailsDao by lazy { AppDatabase.getInstance(ctx).detailsDao() }

fun add(details: FoodDetails) = launch(DB) { detailsDao.insert(details) }

suspend fun getDetails(id: String): FoodDetails? {


return withContext(DB) { detailsDao.loadById(id) } // Prefers database
306 Chapter 8 Android App Development with Kotlin: Nutrilicious

?: withContext(NETWORK) { fetchDetailsFromApi(id) } // Falls back to network


.also { if (it != null) this.add(it) } // Adds newly fetched foods to DB
}

private suspend fun fetchDetailsFromApi(id: String): FoodDetails? {


val request: Call<DetailsWrapper<DetailsDto>> = usdaApi.getDetails(id)
val detailsDto: DetailsDto = withContext(NETWORK) {
request.execute().body()?.foods?.get(0)?.food // Same as before
} ?: return null

return FoodDetails(detailsDto)
}
}

First, the repository has a reference to the DetailsDao to access the database. This is used to add
new entries to the database via this.add. It is also used in getDetails to probe for data. If none
is available in the database, the method falls back to a network call using fetchDetailsFromApi
and caches the retrieved data in the database for subsequent calls. Note that, in a more fleshed-
out implementation, you may want to invalidate database entries after a certain time to allow for
updates of the data.

The code for the network call is taken from DetailsViewModel that now uses the repository as
its single source of truth for data, as shown in Listing 8.110.

Listing 8.110 Using the Repository in the View Model


import com.example.nutrilicious.data.DetailsRepository
// …
class DetailsViewModel(app: Application) : AndroidViewModel(app) {
private val repo = DetailsRepository(app)
suspend fun getDetails(foodId: String): FoodDetails? = repo.getDetails(foodId)
}

As you can see, the view model now simply delegates its task to the repository. Because the reposi-
tory requires a context, the view model now extends AndroidViewModel.

Your app should now cache the details for foods when you first access them. In the emulator, the
network call may take a few seconds to complete so that you should notice subsequent clicks on
the same food to show the data faster. Accordingly, there should be no log entries from network
calls in your Logcat when clicking the same food a second time.
Adding RDIs for Actionable Data 307

Adding RDIs for Actionable Data


In this section, you will further improve this app by making the data more actionable for users.
More specifically, the app should show how much of the recommended daily intake (RDI) each of
the nutrient contents represents. For instance, users should see instantly that 100g of raw spinach
have 20.85% of a person’s daily need of iron.

RDI information will be stored statically in the app in a simple map. To this end, create a new file
RDI.kt in the model package. In order to store the RDIs, Listing 8.111 introduces classes to prop-
erly represent the data.

Listing 8.111 Classes to Represent RDIs


data class Amount(val value: Double, val unit: WeightUnit)

enum class WeightUnit {


GRAMS, MILLIGRAMS, MICROGRAMS, KCAL, IU
}

To keep things simple, the app uses roughly averaged RDIs for adult females and males. In reality,
the RDI depends on age, gender, lifestyle, and other factors. Also, a more accurate representation
would use minimum and maximum targets for each nutrient. For this sample app, we shall be
content with a rough indicator to compare foods.

With the two domain classes above, storing the RDIs is now a matter of writing them down, as
shown in Listing 8.112. As all other code, you can find this on GitHub12 to copy and paste.

Listing 8.112 Storing RDIs Statically


import com.example.nutrilicious.model.WeightUnit.*

internal val RDI = mapOf(


255 to Amount(3000.0, GRAMS), // water
208 to Amount(2000.0, KCAL), // energy
203 to Amount(50.0, GRAMS), // protein
204 to Amount(78.0, GRAMS), // total fat (lipids)
205 to Amount(275.0, GRAMS), // carbohydrates
291 to Amount(28.0, GRAMS), // fiber
269 to Amount(50.0, GRAMS), // sugars
301 to Amount(1300.0, MILLIGRAMS), // calcium
303 to Amount(13.0, MILLIGRAMS), // iron
304 to Amount(350.0, MILLIGRAMS), // magnesium

12. https://github.com/petersommerhoff/nutrilicious-app/blob/master/12_AddingRdisForActionable
Data/app/src/main/java/com/petersommerhoff/nutrilicious/model/RDI.kt
308 Chapter 8 Android App Development with Kotlin: Nutrilicious

305 to Amount(700.0, MILLIGRAMS), // phosphorus


306 to Amount(4700.0, MILLIGRAMS), // potassium
307 to Amount(1500.0, MILLIGRAMS), // sodium
309 to Amount(10.0, MILLIGRAMS), // zinc
401 to Amount(85.0, MILLIGRAMS), // vitamin c
404 to Amount(1200.0, MICROGRAMS), // vitamin b1 (thiamin)
405 to Amount(1200.0, MICROGRAMS), // vitamin b2 (riboflavin)
406 to Amount(15.0, MILLIGRAMS), // vitamin b3 (niacin)
415 to Amount(1300.0, MICROGRAMS), // vitamin b6 (pyridoxine)
435 to Amount(400.0, MICROGRAMS), // folate
418 to Amount(3.0, MICROGRAMS), // vitamin b12 (cobalamine)
320 to Amount(800.0, MICROGRAMS), // vitamin a
323 to Amount(15.0, MILLIGRAMS), // vitamin e (tocopherol)
328 to Amount(15.0, MICROGRAMS), // vitamin d (d2 + d3)
430 to Amount(105.0, MICROGRAMS), // vitamin k
606 to Amount(20.0, GRAMS), // saturated fats
605 to Amount(0.0, GRAMS), // transfats
601 to Amount(300.0, MILLIGRAMS) // cholesterol
)

The new data class Amount is useful to improve the Nutrient class as well. Listing 8.113 combines
its amountPer100g and unit properties into a single new property with type Amount and adjusts
the DTO mapping accordingly.

Listing 8.113 Using the Amount Class in Nutrient


@TypeConverters(NutrientTypeConverter::class)
data class Nutrient(
// …,
val amountPer100g: Amount, // Combines amount and unit into single property
// …
) {
constructor(dto: NutrientDto) : this(
// …,
Amount(dto.value.toDouble(), WeightUnit.fromString(dto.unit)),
// …
)
}

Note that the type converter still works as before and that Room is still able to map this class to
the database, even without a schema change.
Adding RDIs for Actionable Data 309

To make WeightUnit.fromString work, the WeightUnit enum should know which string
each instance corresponds to, such as “g” mapping to WeightUnit.GRAMS. Note that this is
different from Kotlin’s default string representation of each enum instance using valueOf. Thus,
Listing 8.114 adds the fromString method. Additionally, the toString method is overridden to
display the weight unit correctly in the UI later.

Listing 8.114 Mapping WeightUnit to String And Vice Versa


enum class WeightUnit {
GRAMS, MILLIGRAMS, MICROGRAMS, KCAL, IU; // Mind the semicolon

companion object {
fun fromString(unit: String) = when(unit) { // Transforms string to weight unit
"g" -> WeightUnit.GRAMS
"mg" -> WeightUnit.MILLIGRAMS
"\u00b5g" -> WeightUnit.MICROGRAMS
"kcal" -> WeightUnit.KCAL
"IU" -> WeightUnit.IU
else -> throw IllegalArgumentException("Unknown weight unit: $unit")
}
}

override fun toString(): String = when(this) { // Transforms weight unit to string


WeightUnit.GRAMS -> "g"
WeightUnit.MILLIGRAMS -> "mg"
WeightUnit.MICROGRAMS -> "\u00b5g"
WeightUnit.KCAL -> "kcal"
WeightUnit.IU -> "IU"
}
}

Note that the when expression is exhaustive when mapping enum instances to a string but not
the other way around. The logic itself is simply a matter of exploring which units the API returns
and mapping them accordingly.

All required RDI data is now in place. You can use this information to enhance the Details-
Activity. The methods bindUi and makeSection can stay as they are, only renderNutrient
must be adjusted because of the additional information you want to show for each single nutri-
ent. Listing 8.115 shows the extended method.
310 Chapter 8 Android App Development with Kotlin: Nutrilicious

Listing 8.115 Displaying a Nutrient


private fun renderNutrient(nutrient: Nutrient): String = with(nutrient) {
val name = name.substringBefore(",")
val amount = amountPer100g.value.render()
val unit = amountPer100g.unit
val percent = getPercentOfRdi(nutrient).render() // Is implemented next
val rdiNote = if (percent.isNotEmpty()) "($percent% of RDI)" else ""
"$name: $amount$unit $rdiNote"
}

private fun Double.render() = if (this >= 0.0) "%.2f".format(this) else ""

The amount and unit are now extracted from the Amount object. Additionally, the RDI percent-
age is calculated and displayed if it is greater than or equal to zero—in other words, if no error
occurred. The render function is a simple extension on Double that displays it with two
decimal places.

Next, you must implement getPercentOfRdi, which calculates how much of the RDI the
given nutrient’s amount represents. Listing 8.116 shows its implementation.

Listing 8.116 Calculating the Percentage of RDI


private fun getPercentOfRdi(nutrient: Nutrient): Double {
val nutrientAmount: Double = nutrient.amountPer100g.normalized() // Impl. next
val rdi: Double = RDI[nutrient.id]?.normalized() ?: return -1.0

return nutrientAmount / rdi * 100


}

The percentage of the RDI is a simple division of how much of the nutrient the food contains
and what the RDI is. In case no RDI is found, the method returns a negative result due to the
default value of -1.0. If this happens, no percentage is displayed by the render method.

The nutrient values may be given in different units, such as grams and micrograms. Thus, the
Amount class gets a method to normalize the value, as shown in Listing 8.117. This method
should be called before performing any calculations based on this class.

Listing 8.117 Calculating the Percentage of RDI


data class Amount(val value: Double, val unit: WeightUnit) {

fun normalized() = when(unit) { // Normalizes milligrams and micrograms to grams


GRAMS, KCAL, IU -> value
Improving the User Experience 311

MILLIGRAMS -> value / 1000.0


MICROGRAMS -> value / 1_000_000.0
}
}

Kilocalories and international units (IU) require no normalization. As for the weights, this
method normalized them to a value in grams, but you could choose any of the three weight units
as the normalized form.

Users can now immediately see how much of the RDI a food provides and thus choose foods
that satisfy their nutritional needs. This is all the functionality this sample app should have. If
you want to extend it further, you can turn it into a nutrition-tracking app in which users can
enter how much of each food they ate and see what percentage of each RDI they have reached
for that day.

Improving the User Experience


Although this concludes the app functionality, there are a few finishing touches you can make to
improve this app by providing better feedback to the user. Subtleties like this go a long way when
it comes to user experience.

Indicating Empty Search Results


In the search fragment, users are already shown a progress indicator, which is good. But if no
foods are found for a search term, users are not notified and simply presented with an empty
screen. Luckily, it is easy to show feedback in a snackbar if no foods were found. Listing 8.118
adjusts updateListFor to show this feedback.

Listing 8.118 Showing a Snackbar to Indicate Empty Search


private fun updateListFor(searchTerm: String) = launch {
// …
withContext(UI) {
(rvFoods?.adapter as? SearchListAdapter)?.setItems(foods)
swipeRefresh?.isRefreshing = false

if (foods.isEmpty() && isAdded) {


snackbar("No foods found")
}
}
}
312 Chapter 8 Android App Development with Kotlin: Nutrilicious

If a search returns an empty list and the fragment is still attached to its activity, this shows a
snackbar to tell the user that no foods were found for the given search term. The snackbar
method is an extension defined in ViewExtensions.kt (see Listing 8.119).

Listing 8.119 Extension Function to Show Snackbar


import android.support.design.widget.Snackbar
import android.view.View

fun Fragment.snackbar(
msg: String, view: View = activity!!.findViewById<View>(android.R.id.content)) {
Snackbar.make(view, msg, Snackbar.LENGTH_SHORT).show()
}

This method relies on the fragment being attached to its activity because it uses an unsafe call in
its default value. Thus, you must always check isAdded before calling this method, or pass in a
view manually. After importing this function into the search fragment, you can run the app.

Indicate Progress in the Details Activity


Similarly, a progress indicator should be shown in the DetailsActivity to let the user know
the data is being fetched. This activity has no SwipeRefreshLayout, so the first step is to add a
ProgressBar to activity_details.xml as in Listing 8.120. Also, the linear layout is given an
ID in order to show and hide it later when appropriate.

Listing 8.120 Adding a ProgressBar to the Layout


<ScrollView …>

<ProgressBar
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"
style="?android:attr/progressBarStyle" />

<LinearLayout android:id="@+id/content" …>


<!-- sections as before -->
</LinearLayout>

</ScrollView>
Improving the User Experience 313

The progress bar’s default visibility is GONE, meaning that it is not only hidden but does not
affect the layout at all. The logic for showing and hiding it is similar as in the search fragment.
Listing 8.121 introduces a new helper method to DetailsActivity and adjusts updateUiWith
to show and hide the progress bar.

Listing 8.121 Showing and Hiding the Progress Bar


import android.view.View
// …
class DetailsActivity : AppCompatActivity() {
// …
private fun updateUiWith(foodId: String) {
if (foodId.isBlank()) return
setLoading(true) // Indicates that app is loading
launch {
val details = detailsViewModel.getDetails(foodId)
withContext(UI) {
setLoading(false) // Indicates that app finished loading
bindUi(details)
}
}
}

private fun setLoading(isLoading: Boolean) {


if (isLoading) {
content.visibility = View.GONE
progress.visibility = View.VISIBLE
} else {
progress.visibility = View.GONE
content.visibility = View.VISIBLE
}
}
}

The helper method setLoading toggles between showing the progress bar and hiding the
content, and vice versa. With this, the progress bar is shown until the data is received.

Tweaks like these go a long way to improve the app’s usability. Therefore, even though not strictly
Kotlin related, they were included here to indicate aspects that improve user experience.
314 Chapter 8 Android App Development with Kotlin: Nutrilicious

Summary
Having finished this chapter and Chapter 7, you now have two Kotlin apps under your belt, along
with best practices for Android and Kotlin development and a collection of useful extension func-
tions. You are able to implement recycler views, use fragments, and create domain classes, DTOs,
and DAOs concisely, and you have familiarized yourself with essential tools such as Retrofit,
Moshi and the Android Architecture Components. You have also written idiomatic code using
scope operators, delegated properties, immutability where possible, null handling, and other
language features. You can build on all of this in your future apps.
9
Kotlin DSLs

Men build too many walls and not enough bridges.


Joseph Fort Newton

Domain-specific languages (DSLs) regularly come up in discussions related to modern languages


like Kotlin and Scala because these allow creating simple DSLs quickly within the language. Such
internal DSLs can greatly improve code readability, development efficiency, and changeability.
This chapter introduces DSLs and how to create them in Kotlin. It also explores the two most
popular Kotlin DSLs on Android, namely a Kotlin DSL for Gradle build configurations and one for
creating Android layouts.

Introducing DSLs
DSLs are by no means new; research goes back decades1 and is only increasing in recent
times, from language modeling in general2 to DSLs in particular.3 Today, DSLs are pervasive in
software development. This section explains the term and discusses how DSLs can help improve
your code.

What Is a DSL?
As the name implies, a domain-specific language is a language focused on, and often restricted
to, a certain application domain. You are likely familiar with many DSLs already, such as SQL,

1. https://onlinelibrary.wiley.com/doi/abs/10.1002/spe.4380070406
2. http://mdebook.irisa.fr/
3. http://www.se-rwth.de/publications/MontiCore-a-Framework-for-Compositional-Development-
of-Domain-Specific-Languages.pdf
316 Chapter 9 Kotlin DSLs

LaTeX,4 or regular expressions. These are in contrast to general-purpose languages like Kotlin,
Java, C++,5 Python,6 and others.

Focusing on a particular domain allows a more focused and clean syntax and hence better solu-
tions. This is a general trade-off between generic approaches and specific approaches, and it is the
reason why languages like COBOL7 and Fortran8 were initially created with a specific domain in
mind: business processing and numerical computation, respectively.

However, these are different from DSLs written in Kotlin because they are stand-alone languages.
When you create a DSL in Kotlin, it is called an embedded DSL—it is embedded into a general-
purpose language. This has several consequences that come into play when we discuss benefits
and potential drawbacks of these DSLs.

Benefits and Drawbacks


As with any approach or tool, there are both pros and cons to consider when developing or using
DSLs. Van Deursen, Klint, and Visser9 provide a good overview of both in their literature survey.
Here, we view them under the light of embedded DSLs written in Kotlin.

Benefits
The main benefits mentioned by van Deursen et al. are the following:

 Solutions can be expressed on the abstraction level of the domain, enabling domain
experts to understand, validate, optimize, and often even develop their own solutions
in the DSL. This also applies to Kotlin DSLs but (in any case) requires an adequately
designed DSL.
 DSL code is concise, expressive, and reusable. This certainly applies to Kotlin DSLs just
like to other idiomatic Kotlin code.
 DSLs represent domain knowledge and thus facilitate documentation and reuse of this
knowledge. Again, this applies to any adequately designed DSL.

In addition to these, there are several advantages specific to a Kotlin DSL:


 It does not add a new technology to your stack because it is simply Kotlin code.
 As an embedded DSL, it allows you to use any of Kotlin’s language features such as
storing recurring values in variables or using loops inside your DSL.

4. https://www.latex-project.org/
5. http://www.stroustrup.com/C++.html
6. https://www.python.org/
7. http://archive.computerhistory.org/resources/text/Knuth_Don_X4100/PDF_index/k-8-pdf/
k-8-u2776-Honeywell-mag-History-Cobol.pdf
8. http://www.ecma-international.org/publications/files/ECMA-ST-WITHDRAWN/ECMA-9,
%201st%20Edition,%20April%201965.pdf
9. http://sadiyahameed.pbworks.com/f/deursen+-+DSL+annotated.pdf
Introducing DSLs 317

 Because it is pure Kotlin code, your DSL is statically typed, and the excellent
tool support automatically works for your DSL, including autocomplete, jumping to
declarations, showing docs, and code highlighting.

In summary, DSLs are particularly useful where they can embody domain knowledge and reduce
complexity by offering a clean, readable API for developers and domain experts.

Potential Drawbacks
In their survey, van Deursen et al. also mention several drawbacks of DSLs that we will again
examine in the light of Kotlin DSLs. Potential drawbacks of DSLs in general are as follows:
 DSLs can be hard to design, implement, and maintain. This partially applies to Kotlin
DSLs as well, especially the design process for a good DSL must involve domain experts.10
However, as you will see in the examples in this chapter, developers themselves are the
domain experts for the DSLs discussed in this chapter (such as the Kotlin Gradle DSL).
 Educating DSL users can be costly. In the case of Kotlin DSLs, users are either Kotlin
developers who already know the language or domain experts who may need educating but
for whom the DSL greatly facilitates understanding compared to non-DSL code.
 Finding the proper scope for a DSL can be difficult. This is independent of whether you
use a Kotlin DSL or not. Depending on your domain, it may be hard to choose the proper
scope. One way that Kotlin may help is that implementing a prototypical DSL is fast and
thus allows evaluating a DSL with a certain scope and making adjustments to its scope as
needed.
 It can be difficult to balance between domain-specific and general-purpose constructs.
This issue is mostly alleviated in Kotlin DSLs (and any embedded DSL) because you can
focus on the domain-specific aspects and still tap into all of Kotlin’s general-purpose
constructs. In effect, finding the balance is shifted from DSL design to DSL usage because
users may use general-purpose language features to any extent they choose (which can
hamper understandability if overused).
 DSLs may be less efficient than hand-coded software. Fortunately, this does not apply
to Kotlin DSLs because they typically compile to the more verbose code you would write
without the DSL. Because Kotlin DSLs make heavy use of higher-order functions, using
inline functions is essential to avoid any overhead.

Another drawback of embedded DSLs in particular is their limited expressiveness. Being embed-
ded into a general-purpose language, they are naturally constrained in their syntax and in what
they can express. For Kotlin DSLs in particular, this chapter focuses on DSLs that allow construct-
ing objects more easily.

All in all, most of the common drawbacks of DSLs do not apply to Kotlin DSLs or are at least alle-
viated. Still, DSLs are not a golden hammer. Not every object creation or configuration must be
wrapped into a DSL. But if it can be used to encapsulate boilerplate, reduce complexity, or help

10. http://www.se-rwth.de/publications/Design-Guidelines-for-Domain-Specific-Languages.pdf
318 Chapter 9 Kotlin DSLs

domain experts contribute, a DSL can greatly benefit your project. Embedded DSLs, such as in
Kotlin, remove most of the effort involved in creating DSLs and thus greatly reduce the barrier to
introducing DSLs to your workflow (but they are also limited in their capabilities).

Creating a DSL in Kotlin


In this section, you will write your own DSL in Kotlin from scratch to understand the underly-
ing structure. Kotlin DSLs are primarily based on higher-order functions, lambdas with receivers,
default values, and extension functions. The resulting DSL should allow you to create user objects
with the syntax shown in Listing 9.1.

Listing 9.1 User DSL


user {
username = "johndoe"
birthday = 1 January 1984
address {
street = "Main Street"
number = 42
postCode = "12345"
city = "New York"
}
}

As you can see, this syntax to create objects almost resembles JSON. It hides Kotlin as the underly-
ing language (because it avoids using general-purpose language constructs) and hence does not
require users to know Kotlin. Nontechnical team members would be able to understand, validate,
and change the user.

DSL to Build Complex Objects


To explore how such a DSL works, you will build one from the top down. The entry point to the
DSL is the user function. You know this trick by now. It is simply a higher-order function that
accepts a lambda as its last parameter, thus allowing the syntax from Listing 9.1. The user func-
tion can be declared as shown in Listing 9.2.

Listing 9.2 DSL Entry Point


fun user(init: User.() -> Unit): User {
val user = User()
user.init()
return user
}
Creating a DSL in Kotlin 319

The function accepts a lambda with receiver so that the lambda effectively becomes an extension
of the User class. This allows accessing properties such as username and birthday directly from
within the lambda. You may notice that you can write this function a lot better using Kotlin’s
apply function, as shown in Listing 9.3.

Listing 9.3 DSL Entry Point—Improved


fun user(init: User.() -> Unit) = User().apply(init)

The User class is a simple data class, and for this first version of the DSL, it partly uses nullable
fields to keep things simple. Listing 9.4 shows its declaration.

Listing 9.4 User Data Class


import java.time.LocalDate

data class User(


var username: String = "",
var birthday: LocalDate? = null,
var address: Address? = null
)

The code so far accounts for the creation of users with username and birthday in DSL syntax, but
there’s currently no way to nest an address object as in the initial example. Adding the address
is simply a matter of repeating the process, this time with a member function of User as imple-
mented in Listing 9.5.

Listing 9.5 Adding an Address to a User


data class User(…) {
fun address(init: Address.() -> Unit) {
address = Address().apply(init)
}
}

data class Address(


var street: String = "",
var number: Int = -1,
var postCode: String = "",
var city: String = ""
)
320 Chapter 9 Kotlin DSLs

The address function follows the same concept as the user function, but additionally assigns
the created address to the user’s address property. With these few lines of code, you are now able
to create a user in DSL syntax as in Listing 9.1 (except for the date).

However, there are several downsides to the current implementation.

 It relies on angelic developers who carefully initialize every field. But it is easy to forget to
initialize one or assign an invalid value.
 Working with the resulting User object is inconvenient due to its nullable properties.
 The DSL allows undesired nesting of its constructs.

To illustrate this last issue, Listing 9.6 shows an undesirable but currently valid use of the DSL.

Listing 9.6 Shortcomings of Current DSL


user {
address {
username = "this-should-not-work"
user {
address {
birthday = LocalDate.of(1984, Month.JANUARY, 1)
}
}
}
}

This construct mitigates the DSL advantages of readability and reducing complexity. Properties of
the outer scope are visible so that username can be set inside the address block, and both can
be nested arbitrarily. This issue is addressed later in this chapter.

Immutability through Builders


The first aspect to be improved are the nullable and mutable properties. The desired DSL will
effectively be a type-safe builder.11 Accordingly, builders12 are used in the underlying implementa-
tion to accumulate object data, validate the data, and ultimately build the user object. First, write
down the desired data classes, as shown in Listing 9.7.

11. https://kotlinlang.org/docs/reference/type-safe-builders.html
12. https://sourcemaking.com/design_patterns/builder
Creating a DSL in Kotlin 321

Listing 9.7 Immutable Data Classes


import java.time.LocalDate

data class User(val username: String, val birthday: LocalDate, val address: Address)

data class Address(


val street: String,
val number: Int,
val postCode: String,
val city: String
)

This way, because both LocalDate and Address are immutable, the User class is immutable,
too. Objects of these two classes are now constructed by builders so the next step is to add those,
starting with the UserBuilder shown in Listing 9.8.

Listing 9.8 User Builder


class UserBuilder {
var username = "" // Gets assigned directly in DSL => public
var birthday: LocalDate? = null // Gets assigned directly in DSL => public
private var address: Address? = null // Is built via builder => private

fun address(init: AddressBuilder.() -> Unit) { // Nested function to build address


address = AddressBuilder().apply(init).build()
}

fun build(): User { // Validates data and builds user object


val theBirthday = birthday
val theAddress = address
if (username.isBlank() || theBirthday == null || theAddress == null)
throw IllegalStateException("Please set username, birthday, and address.")

return User(username, theBirthday, theAddress)


}
}
322 Chapter 9 Kotlin DSLs

The builder differentiates between properties that should be assigned directly (and are thus
public) and properties that are created using a nested DSL syntax (the address) that are thus
private. The address function is moved from the User class to the UserBuilder, and now
accepts a lambda that has AddressBuilder as its receiver. Also, you can perform arbitrary vali-
dation before finally building the object in build. You could use this to implement optional
properties as well. Next, you need a builder for address objects, as shown in Listing 9.9.

Listing 9.9 Address Builder


class AddressBuilder {
var street = ""
var number = -1
var postCode = ""
var city = ""

fun build(): Address {


if (notReady())
throw IllegalStateException("Please set street, number, postCode, and city.")

return Address(street, number, postCode, city)


}

private fun notReady()


= arrayOf(street, postCode, city).any { it.isBlank() } || number <= 0
}

In this case, there is no more nested construct under address, so the builder only has public prop-
erties for all required data and a build method along with simple validation, but no more nested
builders.

Lastly, the entry point to the DSL must be adapted as in Listing 9.10 to use the builder.

Listing 9.10 Adjusted Entry Point to the DSL


fun user(init: UserBuilder.() -> Unit) = UserBuilder().apply(init).build()

The lambda’s receiver is now UserBuilder. Accordingly, the init function is applied to the
UserBuilder, and calling build is required. So inside init, you can initialize the public proper-
ties directly or call a DSL function to build a more complex object, such as an address.
Creating a DSL in Kotlin 323

Nesting Deeper
The current DSL allows adding an arbitrary number of address blocks, but each one would over-
ride the address from the previous. So a user can currently only have a single address, but multiple
may be desired. There are different ways to design this part of the DSL.
 Check in your DSL if the address function was already called and disallow another call so
that users can only have a single address and the DSL allows only one address block.
 Allow multiple calls to the address function and add each new address to a list.
 Implement a dedicated addresses block that contains all addresses.

For now, let’s assume a user can indeed have multiple addresses but there is no dedicated block to
hold them (the second possibility). One way to achieve this easily is to give the data class a list of
addresses, and then in address, add the built object to that list as done in Listing 9.11.

Listing 9.11 Allowing Multiple Addresses


data class User(…, val addresses: List<Address>)

class UserBuilder {
// …
private val addresses: MutableList<Address> = mutableListOf()

fun address(init: AddressBuilder.() -> Unit) {


addresses.add(AddressBuilder().apply(init).build())
}

fun build(): User { … }


}

This now lets you add multiple address blocks, and each one adds another address to the user
object. Next, a dedicated addresses block should encompass all addresses, yielding the syntax
shown in Listing 9.12.

Listing 9.12 Syntax of Dedicated Addresses Block


user {
username = "johndoe"
birthday = LocalDate.of(1984, Month.JANUARY, 1)
addresses { // New dedicated addresses block
address { // All address blocks must be placed here
street = "Main Street"
324 Chapter 9 Kotlin DSLs

number = 42
postCode = "12345"
city = "New York"
}
address {
street = "Plain Street"
number = 1
postCode = "54321"
city = "York"
}
}
}

Here, addresses can only be created inside the addresses block. To implement such an addi-
tional nesting, you need a helper class, an object of which is built by the addresses block.
Listing 9.13 adds such a class that simply represents a list of addresses.

Listing 9.13 Implementing Dedicated Addresses Block


class UserBuilder {
// …
private val addresses: MutableList<Address> = mutableListOf()

inner class Addresses : ArrayList<Address>() {


fun address(init: AddressBuilder.() -> Unit) {
add(AddressBuilder().apply(init).build())
}
}

fun addresses(init: Addresses.() -> Unit) { // ‘Addresses’ is the receiver now


addresses.addAll(Addresses().apply(init))
}

fun build(): User {


val theBirthday = birthday
if (username.isBlank() || theBirthday == null || addresses.isEmpty()) throw …

return User(username, theBirthday, addresses)


}
}
Creating a DSL in Kotlin 325

This is all you need to enable the syntax shown in Listing 9.12. In general, to allow arbitrarily
deep nesting, you must introduce the appropriate helper classes and methods, like Addresses
and the addresses function in this case. Once you are familiar with creating DSLs like this, you
could even generate (most of) the underlying code because the structure always follows the same
pattern. In fact, JetBrains does this with a React DSL used internally.13

Introducing @DslMarker
This small DSL is now mostly finished, but the problems of arbitrary nesting and accessing prop-
erties of an outer scope remain (see Listing 9.6). To help alleviate the problem of accessing the
outer scope, Kotlin 1.1 introduced the @DslMarker annotation. It’s a meta-annotation that can
be used only on other annotations, such as @UserDsl shown in Listing 9.14.

Listing 9.14 User DSL Annotation


@DslMarker
annotation class UserDsl

Now, whenever there are two implicit receivers of a lambda (for instance, when nesting deeper
than one level in your DSL), and both are annotated with @UserDsl, only the innermost receiver
is accessible. So if you annotate all your DSL classes with it, you can no longer access User prop-
erties inside the address block (or addresses block). It also prevents nesting an address
block into another address block because the address function belongs to the outer receiver
(UserBuilder) and not the innermost one.

What is not prevented this way is calling the user function again from somewhere within the
DSL because it is just a top-level function and thus accessible everywhere. However, this is a
mistake developers are unlikely to make accidentally. If you want to prevent it, add a deprecated
member method to UserBuilder that overshadows the top-level function, as shown in
Listing 9.15.

Listing 9.15 Preventing Nested Calls to Entry Point (in UserBuilder)


@Deprecated("Out of scope", ReplaceWith(""), DeprecationLevel.ERROR)
fun user(init: UserBuilder.() -> Unit): Nothing = error("Cannot access user() here.")

With this, your IDE immediately complains if you try to call user from within another call to
user. It will not compile, either.

13. Source: “Create Your Own DSL in Kotlin” by Victor Kropp (https://youtu.be/tZIRovCbYM8)
326 Chapter 9 Kotlin DSLs

Note
You can follow this same procedure to implement a type-safe builder DSL even if you don’t own
the classes (or if they are implemented in Java) by using extension functions.
Only in the case of annotations, it is a little more tricky because you cannot annotate a third-
party class. Instead, you can annotate the lambda receiver:
fun user(init: (@UserDsl UserBuilder).() -> Unit)
= UserBuilder().apply(init).build()

To enable annotating types as done above, add the corresponding annotation target (also,
retaining the annotation info after compilation is not necessary):

@DslMarker
@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE) // Can be used on types
@Retention(AnnotationRetention.SOURCE)
annotation class UserDsl

Leveraging Language Features


As embedded DSLs, your Kotlin DSLs can make use of Kotlin’s stack of language features. For
example, you could use variables within your DSL naturally and without any extra effort (see
Listing 9.16).

Listing 9.16 Using Variables in Your DSL


user {
// …
val usercity = "New York"
addresses {
address {
// …
city = usercity
}
address {
// …
city = usercity
}
}
}

You could also use conditionals, loops, and other constructs. This automatically allows DSL users
to avoid code duplication within the DSL but requires programming knowledge. Other powerful
Creating a DSL in Kotlin 327

features—such as extensions, infix functions, and operators—can be used to make the code read
even more naturally. As an example, Listing 9.17 enables a more natural way to write dates with
an infix extension.

Listing 9.17 Improving DSL Readability


infix fun Int.January(year: Int) = LocalDate.of(year, Month.JANUARY, this)

user {
username = "johndoe"
birthday = 1 January 1984
// …
}

In reality, you will need 12 extensions to cover all the months, but it can be worth it if your DSL
users have to denote many dates because it prevents potential mistakes from zero-indexing versus
one-indexing the days and months.

With the addition of this extension function, you can now write code as shown in the initial
Listing 9.1, or with a dedicated addresses block if you prefer that convention. But there is
still room for improvement because the DSL currently comes with overhead for lambda objects
created. You can resolve this issue by inlining the higher-order functions (see Listing 9.18).

Listing 9.18 Improving DSL Performance


@UserDsl
class UserBuilder {
// …
val addresses: MutableList<Address> = mutableListOf()

inner class Addresses : ArrayList<Address>() {


inline fun address(init: AddressBuilder.() -> Unit) { … } // Now inlined
}

inline fun addresses(init: Addresses.() -> Unit) { … } // Now inlined


// …
}

inline fun user(init: UserBuilder.() -> Unit) = // Now inlined


UserBuilder().apply(init).build()
328 Chapter 9 Kotlin DSLs

Note that, to inline the addresses function, the addresses property it uses must become
public as well. Otherwise, it may not be accessible in the place where it’s inlined. The other two
higher-order functions can be inlined without further ripple effects. Now, using the DSL has no
performance overhead compared with using the builders directly.

DSL for Android Layouts with Anko


A fantastic use case for such a DSL are layouts. Here, the underlying object created by the type-safe
builder DSL is a root view that encompasses a layout, such as a linear layout or a constraint layout.
Creating a layout programmatically like this has several advantages over XML layouts.
 DSL layouts provide type safety and null safety.
 Building the layout is more efficient compared to XML because it costs less CPU time and
battery life.
 DSL layouts are more reusable; with XML, you would usually at least have to adjust the
element IDs.

In this section, you learn how to programmatically create layouts the hard way first and then
familiarize yourself with Anko Layouts, part of JetBrains’ utility library for Android called Anko.14
To do so, this section covers how to rewrite the layout for the AddTodoActivity from the Kudoo
app using Anko.

Creating Layouts Programmatically


Before introducing Anko, it is worth mentioning that you could create layouts programmatically
without any library. Listing 9.19 shows the code required to implement the AddTodoActivity
layout programmatically without Anko.

Listing 9.19 Creating a Layout Programmatically the Hard Way


class AddTodoActivity : AppCompatActivity() {
// …
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(createView()) // No inflating of an XML layout
viewModel = getViewModel(TodoViewModel::class)
}

private fun createView(): View {


val linearLayout = LinearLayout(this).apply { // Sets up the linear layout

14. https://github.com/Kotlin/anko
DSL for Android Layouts with Anko 329

orientation = LinearLayout.VERTICAL
}
val etNewTodo = EditText(this).apply { // Sets up the EditText
hint = getString(R.string.enter_new_todo)
textAppearance = android.R.style.TextAppearance_Medium
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
val btnAddTodo = Button(this).apply { // Sets up the Button
text = getString(R.string.add_to_do)
textAppearance = android.R.style.TextAppearance
layoutParams = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
).apply { gravity = Gravity.CENTER_HORIZONTAL }
setOnClickListener {
val newTodo = etNewTodo.text.toString()
launch(DB) { viewModel.add(TodoItem(newTodo)) }
finish()
}
}
return linearLayout.apply { // Adds views to the linear layout and returns it
addView(etNewTodo)
addView(btnAddTodo)
}
}
}

You can see that, even though Kotlin’s apply function helps simplify the code quite a bit,
creating a layout like this is quite verbose. There’s no support around setting layout parameters,
defining listeners, or using string resources to set texts. Luckily, you can do better using Anko.

Anko Dependencies
The first way to include Anko in your Gradle project is to use a metadependency that incorporates
all of Anko’s features. Apart from Anko Layouts, this includes Anko Commons, Anko SQLite, and
more. Listing 9.20 shows the corresponding Gradle dependency.
330 Chapter 9 Kotlin DSLs

Listing 9.20 Anko Metadependency


def anko_version = "0.10.5"
implementation "org.jetbrains.anko:anko:$anko_version" // Includes all of Anko

You likely do not need all of Anko’s features, so there are smaller dependencies available. For the
Anko Layout discussed in this section, all you need is given in Listing 9.21.

Listing 9.21 Dependencies for Anko Layouts


implementation "org.jetbrains.anko:anko-sdk25:$anko_version"
implementation "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"

Anko has many fine-grained dependencies for Android support libraries and coroutines, all of
which are listed on GitHub.15

Creating Layouts with Anko


Anko uses the same idea you used in the previous section to make layout creation a lot easier. For
instance, creating a vertical linear layout that contains a button with a listener is achieved with a
few lines of code, as shown in Listing 9.22.

Listing 9.22 Simple Anko Layout


verticalLayout {
button {
text = "Receive reward"
onClick { toast("So rewarding!") }
}
}

The verticalLayout function is a utility to create a LinearLayout with vertical orientation.


By nesting another view inside the lambda, it is automatically added to the containing view.
Here, the button becomes part of the linear layout. Setting the text and adding a click listener is as
simple as assigning the property and using onClick, respectively. The toast function is also one
of Anko’s many utilities, and it works just like the one you wrote yourself.

Adding Layout Parameters


Properties like width, height, margin, and padding are set via layout parameters, for short
lparams in Anko. These are chained to the declaration of a view to define how it should be laid

15. https://github.com/Kotlin/anko#gradle-based-project
DSL for Android Layouts with Anko 331

out inside its parent. As an example, Listing 9.23 makes the button span its parent’s width and
adds margins on all sides to it.

Listing 9.23 Adding Layout Parameters with Anko


verticalLayout {
button { … }.lparams(width = matchParent) {
margin = dip(5)
}
}

Here, the lparams function is chained to the button call. There are many overloads
of lparams, one of which allows setting layout width and height directly as arguments
(inside parentheses). Both are set to wrapContent by default so, in contrast to XML, you
can skip those if the default works for you; Anko provides a matchParent property for the
other cases.

Inside the lparams lambda, you can set margin and padding. As in XML, there are convenient
properties for margin (all sides), verticalMargin, and horizontalMargin (the same goes for
padding). The dip function is used for dp values (density-independent pixels), and there is also
an sp function for text sizes (scale-independent pixels).

Tip
With the Anko Support plugin for Android Studio, you can preview your Anko layouts in Android
Studio’s Design View—if you modularize them into an Anko component:

class ExampleComponent : AnkoComponent<MainActivity> {


override fun createView(ui: AnkoContext<MainActivity>): View = with(ui) {
verticalLayout {
button { … }.lparams(width = matchParent) { … }
}
}
}

Unfortunately, the preview requires a rebuild, so the feedback cycle is significantly slower than
with XML layouts. Also, it currently seems to work with activities only, not fragments. To get the
benefits of both XML and Anko, you can create your layout in XML initially and then migrate it
to Anko. The process is straightforward, and Anko even comes with an XML-to-Anko converter
that you can find in the menu under Code and then Convert to Anko Layouts DSL while inside an
XML layout.
332 Chapter 9 Kotlin DSLs

Migrating Kudoo’s AddTodoActivity to Anko Layouts


Let’s put these concepts into practice by rewriting part of the layout for the Kudoo app. The
simple examples shown so far already cover most of what you need to create a proper layout
with Anko. Following the same structure, you can build up the basic layout as it is used in the
AddTodoActivity, as shown in Listing 9.24.

Listing 9.24 Activity Layout with Anko


class AddTodoActivity : AppCompatActivity() {
// …
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(createView()) // Still no inflating of an XML layout
viewModel = getViewModel(TodoViewModel::class)
}

private fun createView() = verticalLayout { // Sets up vertical linear layout

val etNewTodo = editText { // Sets up EditText and adds it to the linear layout
hintResource = R.string.enter_new_todo
textAppearance = android.R.style.TextAppearance_Medium
}.lparams(width = matchParent, height = wrapContent) {
margin = dip(16)
}

button(R.string.add_to_do) { // Sets up Button and adds it to the linear layout


textAppearance = android.R.style.TextAppearance
}.lparams(width = wrapContent, height = wrapContent) {
gravity = Gravity.CENTER_HORIZONTAL
}.setOnClickListener { // Could also use onClick inside button {…} instead
val newTodo = etNewTodo.text.toString()
launch(DB) { viewModel.add(TodoItem(newTodo)) }
finish()
}
}
}

Here, the layout makes use of variables and assignments naturally within the DSL. Again, this is
a benefit of an embedded DSL. Second, it uses one of the helper properties provided by Anko to
assign resources directly, namely hintResource. This way, you can avoid calls to methods like
getString to read the value of an Android resource. Note that the views no longer require IDs for
this layout; views that are required outside of the DSL code can be made accessible by assigning
them to variables from an outer scope.
DSL for Android Layouts with Anko 333

Modularizing the Anko Layout


The code from Listing 9.24 creates a view (a LinearLayout) that you can directly use inside the
activity’s onCreate method instead of a layout inflater. However, a better approach is to use
AnkoComponent to modularize the code, as shown in Listing 9.25.

Listing 9.25 Modularized Activity Layout Using an Anko Component


class AddTodoActivity : AppCompatActivity() {
// …
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(AddTodoActivityUi().createView(AnkoContext.create(ctx, this)))
viewModel = getViewModel(TodoViewModel::class)
}

private inner class AddTodoActivityUi : AnkoComponent<AddTodoActivity> {

override fun createView(ui: AnkoContext<AddTodoActivity>): View = with(ui) {

verticalLayout {
val etNewTodo = editText {
hintResource = R.string.enter_new_todo
textAppearance = android.R.style.TextAppearance_Medium
}.lparams(width = matchParent, height = wrapContent) {
margin = dip(16)
}

button(R.string.add_to_do) {
textAppearance = android.R.style.TextAppearance
}.lparams(width = wrapContent, height = wrapContent) {
gravity = Gravity.CENTER_HORIZONTAL
}.setOnClickListener {
val newTodo = etNewTodo.text.toString()
launch(DB) { viewModel.add(TodoItem(newTodo)) }
finish()
}
}
}
}
}
334 Chapter 9 Kotlin DSLs

Adding Custom Views


Anko comes with builder functions for all of Android’s views, but what if you have a custom view?
How can you incorporate that into the layout DSL? Fortunately, Anko is extensible in this regard
so that you can extend it with your custom view via extension functions, and the syntax looks
familiar. Assume you have a custom frame layout like the one in Listing 9.26 that ensures that it
always has the same width as height.

Listing 9.26 Custom Frame Layout


import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout

class SquareFrameLayout(
context: Context,
attributes: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attributes, defStyleAttr) {

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {


super.onMeasure(widthMeasureSpec, widthMeasureSpec) // Equal width and height
}
}

You can incorporate this into the Anko Layout DSL by adding an extension function on Android’s
ViewManager that handles its creation, as shown in Listing 9.27.

Listing 9.27 Integrating a Custom Layout into Anko


import android.view.ViewManager
import org.jetbrains.anko.custom.ankoView

inline fun ViewManager.squareFrameLayout(init: SquareFrameLayout.() -> Unit) =


ankoView({ SquareFrameLayout(it) }, theme = 0, init = init)

As you can see, the function signature closely resembles the ones you used to build the User DSL
before; the lambda parameter becomes an extension of your SquareFrameLayout. The ankoV-
iew function is used to create the view, it can optionally apply a theme, and it handles further
initialization based on the lambda expression that is passed in. Its implementation is not much
different from the builder methods in the User DSL. Its first parameter represents a factory so
that you can tell it how to construct the initial object before applying init, here just using
SquareFrameLayout(it). You could also add a theme parameter to your extension function
DSL for Gradle Build Scripts 335

and pass it along to ankoView to allow users to set a theme. With this, you can use squareFra-
meLayout from within the Anko DSL to build an object of this custom view.

Anko Layouts versus XML Layouts


Anko Layouts have several benefits over XML layouts as listed at the beginning of this section—
most notably, type safety and improved performance while saving battery. However, there are also
drawbacks when compared with XML. In parts, they have already become apparent throughout
this section but I will list them again here.
 XML layouts provide faster preview in Android Studio’s design view, hence speeding up the
feedback cycle, which is crucial while working on a layout.
 Autocompletion works faster in XML because the search space is a lot smaller.
 Layouts are automatically separated from business logic. With Anko, you are responsible for
keeping these concerns separated.

In the end, which one is the better choice for your project depends on which of these points you
prioritize. In any case, I recommend starting out with an XML layout until you are satisfied with
it. After that, you can evaluate the possibility of migrating it to Anko.

DSL for Gradle Build Scripts


In 2016, Gradle announced a DSL based on Kotlin as an alternative to Groovy to write build
scripts, and so the Gradle Kotlin DSL16 was born. The main reason for this decision was Kotlin’s
static typing that enables better tool support in Gradle, from code completion and navigation
to the ability to use all of Kotlin’s language features,17 thus making it easier to write build scripts
from scratch.

The Gradle Kotlin DSL is a fair alternative to the usual Groovy build scripts and has its advantages
and drawbacks. It is certainly not a tool you have to use today (at the time of writing); it still
has its weaknesses and not the best documentation. But it is worth exploring, especially in the
context of Kotlin DSLs. So in this section, you’ll rewrite your Nutrilicious build scripts using the
Gradle Kotlin DSL.

Migrating Nutrilicious to Gradle Kotlin DSL


Based on the existing build scripts, you will migrate to the Gradle Kotlin DSL step by step in this
section. This will uncover many similarities and some differences between the Kotlin DSL and the
Groovy DSL.

16. https://github.com/gradle/kotlin-dsl
17. https://blog.gradle.org/kotlin-meets-gradle
336 Chapter 9 Kotlin DSLs

Note
At the time of writing, Android Studio may not immediately recognize the Gradle Kotlin DSL.
In that case, try Refresh All Gradle Projects in the Gradle view, and if that does not help try
restarting Android Studio.

Migrating Gradle Settings


Start migrating the simplest Gradle file, the settings.gradle. For the Nutrilicious app, its defi-
nition in Groovy is just the one line shown in Listing 9.28.

Listing 9.28 settings.gradle (Groovy)


include ":app"

As opposed to Groovy, Kotlin does not allow skipping the parentheses of such method calls, thus
its equivalent in the Kotlin DSL uses parentheses, as shown in Listing 9.29.

Listing 9.29 settings.gradle.kts (Kotlin)


include(":app")

You have to rename the file to settings.gradle.kts to indicate that you are using a Kotlin
script. Nothing else is required, so the project should still build successfully.

Migrating the Root Build Script


Although not as complex as the app module’s build script, the root build script introduces several
new concepts of the Gradle Kotlin DSL.

Build Script Block


The buildscript section defines an extra for the Kotlin version along with repositories and
dependencies. Its Groovy code is shown again for reference in Listing 9.30.

Listing 9.30 buildscript Block (Groovy)


buildscript {
ext.kotlin_version = '1.2.50' // Extra that stores Kotlin version
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
DSL for Gradle Build Scripts 337

This block looks similar using the Gradle Kotlin DSL shown in Listing 9.31.

Listing 9.31 buildscript Block (Kotlin)


buildscript {
extra["kotlin_version"] = "1.2.50"
repositories {
jcenter()
google()
}
dependencies {
classpath("com.android.tools.build:gradle:3.1.3")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${extra["kotlin_version"]}")
}
}

The only notable difference is that extras are defined using extra["key"] = value, and
accordingly, they must also be accessed via the extra. Also, you cannot omit the parentheses when
calling the classpath function.

All Projects Block


The allprojects block is in fact the exact same in both DSLs (see Listing 9.32).

Listing 9.32 allprojects Block (Groovy and Kotlin)


allprojects {
repositories {
jcenter()
google()
}
}

Delete Task
The syntax to create tasks is slightly different. In Groovy, the clean task is defined as shown in
Listing 9.33.

Listing 9.33 Delete Task (Groovy)


task clean(type: Delete) {
delete rootProject.buildDir
}
338 Chapter 9 Kotlin DSLs

Kotlin uses a higher-order function to create tasks. It accepts the task type as a generic parameter,
the task name as a string, and a lambda defining the task (see Listing 9.34).

Listing 9.34 Delete Task (Kotlin)


task<Delete>("clean") {
delete(rootProject.buildDir)
}

Other than that, the only difference is again the syntax for method calls. This is all that’s required
to migrate the root build.gradle file to the Gradle Kotlin DSL. To make it work, rename the file
to build.gradle.kts. Android Studio should recognize it as a Gradle build script.

Migrating the Module Build Script


The module’s build script is longer than the other scripts, but the migration is mostly
straightforward.

Plugins
The syntax to apply plugins is quite different. Instead of writing apply plugin: 'my-plugin'
on the top level for each one, Kotlin introduces a plugins block, as shown in Listing 9.35.

Listing 9.35 Applying Plugins (Kotlin)


plugins {
id("com.android.application")
id("kotlin-android")
id("kotlin-android-extensions")
id("kotlin-kapt")
}

Using id, you can use the same string as in Groovy to identify the plugins. Alternately, you could
use kotlin, which prepends "org.jetbrains.kotlin." to the given plugin. For instance,
you could use kotlin("android") instead of id("kotlin-android"). A full list of plugins
under org.jetbrains.kotlin is available via the Gradle plugin search.18 Personally, I prefer the
consistent look of using only id.

Android
Next comes the android block. As shown in Listing 9.36, its definition in Kotlin is similar to the
Groovy way.

18. https://plugins.gradle.org/search?term=org.jetbrains.kotlin
DSL for Gradle Build Scripts 339

Listing 9.36 Android Setup (Kotlin)


android {
compileSdkVersion(27)
defaultConfig {
applicationId = "com.example.nutrilicious"
minSdkVersion(19)
targetSdkVersion(27)
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
proguardFiles("proguard-rules.pro")
}
}
}

It is not always obvious which properties can be set using property access and which require a
method call. Fortunately, autocompletion helps with this. In the case of SDK versions, the Kotlin
DSL requires method calls because the property type is ApiVersion under the hood and the
methods are helpers to create these objects from the given integer.

Note that existing build types are accessed using getByName. Creating a new build type works the
same way using create("buildtype") { … }. Product flavors are accessed and created with
these same two methods—but in a productFlavors block—as you can find out using autocom-
plete thanks to static typing.

Dependencies
Adding dependencies in the Gradle Kotlin DSL is straightforward: Here, we omit dependencies
that don’t show a new concept and focus on a few different ones, shown in Listing 9.37.

Listing 9.37 Adding Dependencies (Kotlin)


dependencies {
val kotlin_version: String by rootProject.extra // Uses extra from root script
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version")
// …
val moshi_version = "1.6.0"
implementation("com.squareup.moshi:moshi:$moshi_version")
340 Chapter 9 Kotlin DSLs

kapt("com.squareup.moshi:moshi-kotlin-codegen:$moshi_version")
// …
testImplementation("junit:junit:4.12")
androidTestImplementation("com.android.support.test:runner:1.0.2")
androidTestImplementation("com.android.support.test.espresso:espresso-core:3.0.2")
}

Extras defined in the root build script are accessed by using the rootProject.extra map as a
delegate (remember you can use maps as delegates). Other than that, you simply use val instead
of def and add parentheses to method calls. The method names themselves are the same as in
Groovy—for example, implementation and kapt—so they are easily discoverable.

Experimental Features
The last block in the script enables experimental Kotlin Android Extensions. Unfortunately, at the
time of writing, enabling experimental Android extensions does not work in the Kotlin DSL as
expected. But the DSL allows you to inject Groovy closures at any point, meaning you can always
fall back to Groovy to circumvent such issues, as done in Listing 9.38.

Listing 9.38 Enabling Experimental Android Extensions (Kotlin)


androidExtensions {
configure(delegateClosureOf<AndroidExtensionsExtension> { // Injects Groovy code
isExperimental = true
})
}

The given lambda is transformed to a Groovy Closure<AndroidExtensionsExtension>,


where the AndroidExtensionsExtension is also the receiver of the androidExtensions
lambda. So the closure is simply passed along. By the time you read this, the issue may have been
resolved so I recommend trying it without the configure call first.

Using buildSrc in Gradle


One way to modularize your Gradle builds is to make use of Gradle’s buildSrc directory. If you
add it under the project root, Gradle will compile it and add all its declarations to the classpath
of your build scripts. The buildSrc directory is placed in the project’s root directory (next to
the app directory). Its structure is the same as for any module, including a Gradle build file of its
own and the directory buildSrc/src/main/java. Inside this java directory, add a new file
GradleConfig.kt. Any declarations in this file will be available in your build script so you can
extract all your versions and dependencies into this file, as shown in Listing 9.39.
DSL for Gradle Build Scripts 341

Listing 9.39 Gradle Config in buildSrc


private const val kotlinVersion = "1.2.50"
private const val androidGradleVersion = "3.1.3"

private const val supportVersion = "27.1.1"


private const val constraintLayoutVersion = "1.1.0"
// All versions as in build.gradle.kts…

object BuildPlugins {
val androidGradle = "com.android.tools.build:gradle:$androidGradleVersion"
val kotlinGradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
}

object Android {
val buildToolsVersion = "27.0.3"
val minSdkVersion = 19
val targetSdkVersion = 27
val compileSdkVersion = 27
val applicationId = "com.example.nutrilicious"
val versionCode = 1
val versionName = "1.0"
}

object Libs {
val kotlin_std = "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
val appcompat = "com.android.support:appcompat-v7:$supportVersion"
val design = "com.android.support:design:$supportVersion"
// All dependencies as in build.gradle.kts…
}

This file now encapsulates all the concrete versions and dependencies of the app. To scope the
properties, you can place them in corresponding objects.

Next, to enable Kotlin in this module, add the build.gradle.kts script from Listing 9.40
directly into the buildSrc directory.
342 Chapter 9 Kotlin DSLs

Listing 9.40 buildSrc/build.gradle.kts


plugins {
`kotlin-dsl` // Uses ticks: ``
}

All declarations from the GradleConfig file can now be used in the build script. For the sake of
brevity, Listing 9.41 only shows snippets, but the full source is available in the GitHub repository
for this book.19

Listing 9.41 Using the Gradle Config


android {
// …
targetSdkVersion(Android.targetSdkVersion) // Uses the ‘Android’ object
versionCode = Android.versionCode
// …
}

dependencies {
// …
implementation(Libs.moshi) // Uses the ‘Libs’ object
kapt(Libs.moshi_codegen)
// …
}

The BuildPlugins object can be used in the same way in the root build script. Note that you
don’t need to import anything to use the declarations from buildSrc in your build scripts.

Benefits and Drawbacks


The benefits of the Gradle Kotlin DSL were already mentioned and are based on tool support
through static typing. Android Studio can autocomplete available functions for further nesting,
and which other properties and methods can be called. This is especially helpful when writing or
extending a build script.

On the other hand, at the time of writing, Android Studio may not correctly discover the DSL
methods when migrating to the Gradle Kotlin DSL. As mentioned, a restart usually solves this
problem. Unfortunately, at the time of writing, Android Studio also does not seem to pick up on
the settings.gradle.kts file correctly and does not show it under Gradle Scripts.

19. https://github.com/petersommerhoff/kotlin-for-android-app-development
Summary 343

When using Gradle’s buildSrc, Android Studio currently needs a rebuild to reflect updates to the
files and provide the correct autocompletions. Also, Android Studio will no longer provide hints
to indicate that newer versions of dependencies are available (but there is a Gradle plugin to do
this20). If you currently use the root build script to store version and dependency info, following
this approach can significantly clean up your root build script.

Most of these issues refer to Android Studio and should be resolved in future releases. In summary,
it is convenient to use Kotlin as a single language (that you know well) for both logic and build
scripts. But considering the current tool limitations, sticking to Groovy is a reasonable choice.

Summary
You can now create your own simple Kotlin DSLs from scratch by combining higher-order func-
tions, extensions, infix functions, and other language features. You have also seen how this
concept is applied to Android layouts with Anko and Gradle build scripts with the Gradle Kotlin
DSL. These two are currently the most prevalent Kotlin DSLs for Android development. Both have
their advantages and drawbacks you have to weigh before deciding on which approach to use.
In any case, Kotlin DSLs are a powerful tool to add to your toolbox to create even cleaner APIs to
build complex objects or configurations.

20. https://github.com/ben-manes/gradle-versions-plugin
This page intentionally left blank
10
Migrating to Kotlin

A language that doesn’t affect the way you think about


programming is not worth knowing.
Alan J. Perlis

Migrating to a new programming language can be daunting. This chapter gives practices that
have helped other companies successfully migrate. But because a migration is not just a techni-
cal matter, this chapter also covers the nontechnical aspects involved, such as communication,
getting buy-in, and risks to consider.

On Software Migrations
Changing any tool, technology, or technique involved in the software development process is not
just a technical decision because it also affects business concerns, such as deployment cycles, user
satisfaction, and estimated project effort. For these reasons, a migration requires buy-in from the
whole team as well as the responsible manager(s).

Also, as a technical person, it is important to keep in mind that, from a business perspective, many
more aspects come together in a project that affect its chance to succeed. Therefore, when pitching
the adoption of a new tool like a programming language to your manager, you should have a realis-
tic view of what impact this tool can have, taking into consideration all that makes up the project.

Risks and Benefits


Every migration or tool change has inherent risks. This means that there is always a certain
barrier to introducing change, and a certain “activation energy” necessary to trigger change.
Risks exist on the technical and the business level. On the technical level, risks include problems
integrating with the toolchain or the existing technology stack, unexpected roadblocks, leaving
behind experience with the previous tool or technology, and transferring knowledge. On a busi-
ness level, risks include negative effects on productivity, team and user satisfaction, and keeping
deadlines even during the transition phase.
346 Chapter 10 Migrating to Kotlin

Each of these risks can also be turned into a benefit if the effects of the migration are predomi-
nantly positive. Namely, increasing satisfaction and motivation, fewer roadblocks and smoother
development, better interoperability with the current technology stack, and so forth. As a rule of
thumb, migrating to a new tool or technology will likely slow you down in the short term, but
this initial slowdown should repay in the medium or long term. Of course, this can make it hard
to struggle through the migration and justify its necessity—which is why buy-in is crucial before
attempting to migrate to any new technology or tool.

Note
Think about each of these general points in terms of migrating from Java to Kotlin to contem-
plate benefits and risks it may have in your opinion.

Generally, a migration must have a defined purpose—and adopting a new technology because
it is becoming popular is not a purpose. This is not to imply that adopting Kotlin doesn’t have a
purpose—it can have many benefits as you will see. Good questions to ask before any migration
include the following.
 Is the tool, technology, or technique mature and proven to be effective in practice?
 Are other companies successful with it?
 Does it have a supportive community?
 Do you have experts in the team who can facilitate the adoption?
 Is the team interested in using (and learning) the new tool or technology?
 Does it integrate well with the current technology stack and tooling?

In the case of Kotlin, it has been proven mature enough to be incorporated into the technology
stack; many companies have done so successfully. These also contribute to Kotlin’s very active
community. The other questions depend on your company and team, but can also be affected by
you—you can be the expert facilitating the change, and convince your team of the benefits Kotlin
can have to spark interest in the language and the motivation to learn it.

In terms of tooling for Kotlin, there are certainly obstacles to overcome. For instance, compiler
plugins and static analysis tools are not as abundant for this young language as they are for Java.
Additionally, while Gradle is pushing forward with its excellent Kotlin support, other build tools
do not offer special Kotlin support and may not play well with language features that don’t exist
in Java. The same can be said for several libraries such as Gson (a JSON mapper), which cannot
handle concepts like primary constructors. While Gson can be replaced by Moshi when using
Kotlin, not all libraries may have such a direct counterpart. Thus, the best way to evaluate Kotlin
is to test it with your tech and tool stack to explore possible issues.

Leading the Change


Bringing about a tool change is a task that requires leadership, communication, and ultimately
convincing. Especially when it comes to programming languages, developers tend to have strong
Leading the Change 347

opinions and preferences because it’s one of the most immediate tools they constantly work
with—just like the IDE and the version control system. Thus, adopting a new language may be
one of the hardest changes if your team doesn’t like the new language—or one of the easiest if you
can get them excited about the new language. This section provides guidance and actionable tips
to lead the change.

Getting Buy-In
Adopting a new programming language requires buy-in from everyone involved—but how can
you get that buy-in? This depends heavily on whom you want to convince (a technical versus
business person) and at what kind of company you work (a startup versus a large corporation).
Ultimately, with buy-in, developers are more likely to push through the obstacles that will occur
during migration.

Here, I’ll loosely differentiate between “technical people” and “business people” to give examples
for arguments addressing both perspectives. Mostly, it is about framing the same underlying
benefit in the correct context. Arguments you can use to entice the use of Kotlin include the
following.
 Fewer null-pointer exceptions: Even after full migration to Kotlin, unfortunately you
cannot guarantee complete null safety because you’re still interoperating with Java in the
Android SDK and likely in several dependencies. Still, Kotlin helps reduce null-pointer
exceptions which, from a business perspective, means fewer app crashes, more ads shown,
better user retention, and so forth.
 Smaller pull request and code reviews: With fewer lines of (readable) code, developers
can review code faster. Also, you need less time to think about null cases in reviews if you
use mostly non-nullable types in Kotlin and can focus on more interesting aspects instead.
From a business perspective, faster reviews mean increased productivity and higher code
quality.
 Map language features to issues: Be perceptive about common issues in your code base
and link them to Kotlin features that can help mitigate such issues so that you have
concrete examples for how Kotlin would improve the code base. Christina Lee used this
approach, among others, to introduce Kotlin at Pinterest in 2016.1
 Excited and motivated developers: Refer to surveys2,3 that indicate that Kotlin users love
the language4,5 and therefore tend to be more motivated and more productive.

1. https://www.youtube.com/watch?v=mDpnc45WwlI
2. https://pusher.com/state-of-kotlin
3. https://www.jetbrains.com/research/devecosystem-2018/kotlin/
4. https://insights.stackoverflow.com/survey/2018/#technology-most-loved-dreaded-and-
wanted-languages
5. https://zeroturnaround.com/rebellabs/developer-productivity-report-2017-why-do-you-use-
java-tools-you-use/
348 Chapter 10 Migrating to Kotlin

 Removing third-party dependencies: If migrating to Kotlin fully, libraries like


Retrolambda,6 Butter Knife,7 Lombok, and AutoValue8 are no longer necessary in
the long term, also reducing the method count and APK size.
 Officially supported: Last but not least, don’t forget to mention Kotlin is an official
language on Android backed by Google. This and the fact that JetBrains has a large team
working on the language means that it’s not going away anytime soon. For the business,
this means it is a stable tool that is unlikely to become legacy in a few years.

These are by no means all the benefits. You can pour out all that you’ve learned in this book to
give an accurate picture of Kotlin’s advantages and potential drawbacks, then team up with others
excited about Kotlin to lead the change. With enough buy-in from your team members, you can
pitch the idea to everyone involved, for instance, by composing a brief document that pitches the
language—like Jake Wharton did to lead the adoption at Square.9

Although you want to focus on the benefits here, you must manage expectations. Obviously, you
should paint an accurate picture of the language and communicate clearly that adoption comes
with a learning curve that will initially slow down the team but should be amortized later. Also,
the decision to migrate must be evaluated thoroughly; keep in mind that not migrating may be
the better choice for you or your company.

Sharing Knowledge
Sharing knowledge is particularly essential before and when starting the migration in order to
inform people about the technology and what it can do. This should be planned for ahead of time
as it will take time and effort across the team. Approaches to share knowledge successfully include:
 Pair programming: This allows instant feedback between colleagues and sparks discussions
about best practices, idiomatic code, and so forth. Consider pairing a more experienced
Kotlin developer with a language learner to speed up knowledge transfer.
 Organize talks, presentations, and workshops: These can be internal, in user groups, or
one of the big Kotlin conferences like KotlinConf.10
 User groups: If you do not have a user group in your area yet, consider founding one to
bounce ideas off likeminded developers, learn from them, and let them learn from you. If
there is one already, it’s an ideal way to expose yourself and your team to the language and
community.
 Promote collective discussions: Especially while people familiarize themselves with
Kotlin, it’s important to address their doubts, ideas, questions, and opinions. Discussions
with the whole team allow carving out agreed-upon practices, conventions, and action
plans for the migration.

6. https://github.com/luontola/retrolambda
7. http://jakewharton.github.io/butterknife/
8. https://github.com/google/auto/tree/master/value
9. https://docs.google.com/document/d/1ReS3ep-hjxWA8kZi0YqDbEhCqTt29hG8P44aA9W0DM8/
10. https://kotlinconf.com/
Partial or Full Migration 349

 Document knowledge: An internal wiki or another easily accessible resource is a great


place to document best practices, benefits, risks, and all aspects affecting the migration.
 Tap into the community: The best way to succeed with migration is to learn from people
who have done it. Luckily, the Kotlin community is extremely active and supportive, so
remember to use it. Personally, I consider the Slack channel to be the primary place to tap
into the community.11

Partial or Full Migration


Let’s assume you have successfully pitched Kotlin adoption, or maybe you just decided to migrate
a pet project of yours. You need a migration plan. This section discusses advantages and disadvan-
tages of the two main types of migration—partial migration versus full migration. As you will see,
these have quite different consequences.

Partial Migration
Partial migration means that you mix Kotlin and Java in your project. Even so, you get several
benefits:
 Reduced lines of code: This affects overall code base size, code reviews, and pull requests.
 The feeling of having Kotlin deployed: This shows feasibility and builds trust that
Kotlin can be used in production and deployed to clients without problems.
 Experience gain: Any exposure to Kotlin, especially writing code yourself, increases
knowledge and experience. The more you migrate, the more proficient you become.

These are on top of all of Kotlin’s benefits that you already know of. Unfortunately, partial migra-
tion and a polyglot code base with mixed languages comes at a cost. The most important draw-
backs to keep in mind include the following:
 Harder to maintain: Constantly switching between two languages in your code base
means a lot of context switching and thus cognitive overhead. Also, in the case of Kotlin
and Java, you will likely find yourself writing constructs of one language in the other from
time to time because they look so similar.
 Harder to hire people: There are far fewer Kotlin developers than Java developers so,
from a business perspective, it is important to be aware that it is harder to find proficient
developers, and you may have increased onboarding time on the project.
 Increased build times: Mixing Kotlin and Java in your project will increase compilation
and build times. Assuming you’re using Gradle, incremental build times don’t increase as
much as clean build times, and Kotlin build times have also improved significantly. The
first converted file has most impact, and each following Kotlin file does not significantly
affect build time anymore. Figure 10.1 demonstrates why this effect occurs. Basically,

11. https://kotlinlang.org/community/
350 Chapter 10 Migrating to Kotlin

introducing just one Kotlin file requires the Kotlin compiler to compile all Java files that it
depends on, which takes a noticeable amount of time.

Migrating module by module reduces this increase in build time and reduces the number of
integration points and thus context switches. As mentioned, build tools other than Gradle
may show more significant increases in build time because Gradle specifically works on its
Kotlin integration.
 Interoperability issues: Although Kotlin interoperates well with Java, there are still several
things to consider—which is why this book has a whole chapter dedicated to interoperability.
If at least your own code is entirely in Kotlin, many interoperability issues disappear.
 Hard to reverse: Migrating and converting files from Java to Kotlin is easy but can be hard
to reverse; in fact, it would require rewriting the file from scratch if too many changes have
been made so that version control becomes useless.

*.kt

Kotlin Compiler Kotlin .class files


Java Compiler All .class files
kotlinc javac

*.java

Figure 10.1 Compilation process of mixed-language project. Kotlin compiler uses both Java and
Kotlin source files to link them properly but only emits class files for Kotlin source files

In short, a polyglot code base introduces several drawbacks that may outweigh the benefits
of introducing Kotlin in your project. Therefore, it is important to realistically evaluate the pros
and cons before doing a partial migration (which may be the only feasible option of migration).
In large projects, even if your goal is to migrate entirely, you may have to live with these issues
for a considerable amount of time. In smaller projects, where it can be done in a reasonable
amount of time, I would recommend focusing on migrating the entire code base quickly
(or not migrating at all).

Full Migration
All of the benefits of partial migration above also apply to full migration, as well as some of the
drawbacks.
 It is still harder to hire people but at least developers must be proficient only in Kotlin.
 The migration is hard to reverse once you’ve introduced enough changes in Kotlin so that
the last Java version in version control is useless.
 Interoperability issues are not entirely mitigated because at least the generated R file and
BuildConfig continue to use Java, as well as likely some of the libraries you use.
Where to Start 351

However, dedicating to a full migration brings great benefits. On top of all the benefits listed for
partial migration, these include:
 Better build times because all your own modules use exclusively Kotlin, taking full benefit
of the reduction in build time this creates.
 A system that is easier to maintain because single-language production and test code
requires fewer context switches, introduces fewer interoperability issues, and allows faster
onboarding.

In summary, aim for a full migration if you do decide to adopt Kotlin at your company. For small
projects, this can be done in a relatively short time. In larger projects, it may well be that a full
migration is not feasible due to third-party dependencies, internal restrictions, or simply the
effort involved. In these cases, you can still follow a migration plan as outlined below.

In any case, you should have an agreed-upon migration plan that everyone involved follows, and
you should introduce Kotlin ideally module by module and for each module package by package
to minimize build time, the number of integration points, and context switches.

Where to Start
If you decide to adopt Kotlin, whether partially or fully, the next question becomes: “Where do I
start?” This section covers three ways to start integrating Kotlin into your code base, along with
the respective advantages and disadvantages.

Test Code
The first possibility is to start writing or migrating test cases in Kotlin. At the time of writing, this
is proposed on the Android Developers website12 and many companies have successfully used this
approach. Test code is rather simple, so this is an easy place to start trying out Kotlin. It’s also rela-
tively easy to migrate back to Java.

However, there are many arguments against this approach. There are two basic scenarios: You are
adding new test cases or migrating existing ones. In the first scenario, you’re adding new tests in
Kotlin for existing functionality—so you’re testing after the fact and not in a test-first manner.
Also, this only works if you had gaps in your tests in the first place. In the other scenario, you’re
migrating existing Java test cases to Kotlin. This incurs several risks.
 Bugs that you unknowingly introduce into the test code can lead to production bugs
in the corresponding functionality—and introducing such bugs can happen especially
while you don’t have much experience with Kotlin yet. For instance, consider the subtle
difference given in Listing 10.1 that may cause a bug without your knowing.
 You don’t refactor test cases as often as your production code. Thus, an intricate bug in
the test code is likely to remain for a long time.

12. https://developer.android.com/kotlin/get-started#kotlin
352 Chapter 10 Migrating to Kotlin

Listing 10.1 Subtle Difference


val person: Person? = getPersonOrNull()

if (person != null) {
person.getSpouseOrNull() // Let's say this returns null
} else {
println("No person found (if/else)") // Not printed
}

person?.let {
person.getSpouseOrNull() // Let's say this returns null
} ?: println("No person found (let)") // Printed (because left-hand side is null)!

 Test cases are not tested themselves, so you are basically operating in the dark. Most
test code is rather simple, but if you do make a mistake, you are less likely to notice.
 In test code, you cannot usually make full use of Kotlin’s powerful language features
that provide actual benefits. In fact, if you only rewrite your JUnit13 test cases with Kotlin
using JUnit, you won’t gain much at all—you would mostly be writing Java-like code in
Kotlin, not improving your code quality much at all.

All in all, although test code has become a popular starting point to introduce Kotlin, I personally
consider the following two approaches preferable.

Production Code
I’ll assume here that your production code is thoroughly tested so that changes can be made with
confidence, and if a bug is introduced during migration, one of the test cases should fail. This
greatly supports migration because you know if you did something wrong. Other benefits include
the following.
 You’re implementing actual product features, which is not only a lot more motivating
but also gives you the confidence to know that you can deploy Kotlin to your users.
 You can make full use of Kotlin’s language features, even those that are rarely used in test
code, such as sealed classes, coroutines, and delegated properties. You can compare your
implementation directly with the previous one in Java to see the benefits.
 You can start with very simple features or functionality so that you are unlikely to make
mistakes, then work your way up to migrating the more complex parts of your app.
 Your production code is refactored regularly so that even if you did introduce a bug or
wrote unclean or unidiomatic code, it is more likely to be fixed soon after.

13. https://junit.org/
Where to Start 353

In summary, don’t assume that migrating test code is the safest way to start migrating. If you have
a strong test suite, making changes to your production code is a lot safer because you get direct
feedback in case you introduce a bug.

If you do not yet have a (good) test suite, then adding new tests in Kotlin first is a reasonable alter-
native. Adding them after the fact is still better than not testing at all, and you can combine this
with migrating the corresponding production code.

Pet Projects
Pet projects are probably the best way to gain experience with Kotlin once you’re familiar with
the language—which you certainly are after working through this book. You should work on pet
projects by yourself to further familiarize yourself with the language before pitching it to your
company. If other team members are interested in evaluating Kotlin, it is the perfect chance to
work on a pet project together. If you’re considering adopting Kotlin at your company, work
on the pet project with the team that would be affected by the adoption—ideally also using
the same technology stack to encounter possible problems ahead of time. For instance, Kotlin
does not play well with Lombok. So if you have a large project using Lombok where you cannot
easily migrate all Lombok uses, you’ll have to think about how to deal with this incompatibility
beforehand.

On the downside, this approach costs effort and time without direct progress on company prod-
ucts. But there are many benefits to pet projects that can make them well worth the investment.
 Pet projects provide a safe environment to compare different solutions and conventions,
such as finding an adequate balance between conciseness and expressiveness, how much
to use functional programming concepts, or what can be solved easily using the standard
library.
 Each team member has the chance to evaluate Kotlin’s drawbacks and benefits for
himself or herself and discuss them with teammates.
 You can use pair programming in pet projects to accelerate knowledge transfer from
people already familiar with Kotlin and to spark discussions.
 You can collect data (ideally with the same technology stack) to discover potential
problems ahead of time. To measure build times, you can use the Gradle Profiler.14
 You will come across issues early on—and before using Kotlin in production. Whether
this is not being able to run individual test cases with Spek15 (a testing framework for
Kotlin), Mockito16 not always playing smoothly with Kotlin (MockK17 is a good alternative),
or the tendency of tooling to be behind a bit.

14. https://github.com/gradle/gradle-profiler
15. https://spekframework.org/
16. https://site.mockito.org/
17. http://mockk.io/
354 Chapter 10 Migrating to Kotlin

Pet projects are incredibly effective to evaluate Kotlin before even considering a migration. It
not only gives you the opportunity to research build tool integrations, third-party libraries, and
other integration points; it also allows you to start developing internal libraries that encapsulate
common use cases in well-defined APIs and would be useful in future projects. What’s more, it
gives you a chance to evaluate testing best practices and test infrastructure.

Generally, I’d recommend starting off with pet projects in the team and, if Kotlin should be
adopted, start with simple and well-tested functionality in a non-business-critical app.

Make a Plan
The previous sections already outlined general practices that can all be part of a migration plan.
Here, we summarize and extend upon them again as an overview.
 Start with simple and thoroughly tested functionality where you are unlikely to
introduce bugs without noticing.
 Migrate module by module, and within that, package by package to improve build times
and reduce integration points.
 Plan when to migrate test code and evaluate testing frameworks and infrastructure for
Kotlin in pet projects.
 Isolate Kotlin’s API for higher-level Java consumers to avoid interoperability issues from
using Kotlin’s standard library or own APIs from Java.
 Write all new features in Kotlin and enforce this in pull requests.
 Consider migrating every file that you touch to fix a bug or to refactor it.
 Block dedicated time to focus on migration in larger projects, for instance, in every
sprint if you are using Scrum.

These general rules help guide the process. SoundCloud18 and Udacity,19 for example, both
followed the last three points when adopting Kotlin.20,21 Agree on a clear set of rules with your
team, and work out a concrete migration plan that follows the above ideas.

Tool Support
The Java-to-Kotlin converter is a useful tool to speed up migration. This section covers how to use
it, what to do after using it, what to take heed of, and general tips to facilitate migration.

18. https://soundcloud.com/
19. https://udacity.com/
20. https://fernandocejas.com/2017/10/20/smooth-your-migration-to-kotlin/
21. https://engineering.udacity.com/adopting-kotlin-c12f10fd85d1
Tool Support 355

Java-to-Kotlin Converter
The converter is bundled into the Kotlin plugin so it’s accessible in Android Studio and IntelliJ by
default. It is useful not only to make quick progress when integrating Kotlin but also to learn the
ropes for beginners by comparing the generated code to the original Java code.

You can trigger the converter in different ways. First, you can invoke it under Code and then
Convert Java File to Kotlin File in Android Studio’s menu to convert the current file. Second,
whenever you paste code from a Java file into a Kotlin file, Android Studio will automatically
prompt you to convert the code. Third, although this action is currently named Convert Java File
to Kotlin File, it can convert whole packages, modules, or even projects. So you can right-click on
any directory in the project view and trigger the action from there to recursively convert all its
Java files.

Note
Don’t autoconvert large parts of your code base without a plan and the time to go through and
refactor all converted files. Even then, I’d still recommend doing the conversion file by file to
migrate a package or module to have better control over the process.

Adjusting Autoconverted Code


Irrespective of how you decide to use the converter, you will have to adjust most converted code to
follow best practices, to use idiomatic patterns, and to improve readability. After all, there is only
so much an automated tool can do. Here, we provide a checklist of common changes you’ll have
to make.
 Avoid the unsafe call operator (!!) wherever possible—and do not hesitate to restructure
the code to avoid nullability in the first place.
 Move helper methods to file level where appropriate.
 Avoid overuse of companion objects for all that was static in Java; consider using top-level
declarations instead, and consider using const on top-level variables.
 Join property declaration and initialization—Android Studio will suggest this as well.
 Decide whether to keep @Throws annotations for methods. Recall that this is useful if
they are also called from Java or for documentation purposes.
 Use function shorthand syntax where it is possible and improves readability.

More high-level questions to ask include:


 Which methods would better be extension functions? Especially when converting a
utility class, you will likely want to turn its helper methods into extension functions. But
other methods may also benefit from the transformation.
 Can I use delegated properties? For instance, if you’re building a complex object that is
only used under certain conditions, put it into a lazy property.
356 Chapter 10 Migrating to Kotlin

 Can more declarations be immutable? Rethink any use of mutable data and var to abide
by Kotlin’s mentality. This of course should be done in Java code as well.
 Can I use read-only collections? Although they’re not strictly immutable, prefer Kotlin’s
read-only collections to mutable ones. As Java only has mutable collections in its standard
library, the converter will keep them and use types like ArrayList.
 Could an infix function or operator improve readability? You should use these
judiciously (especially operators), but if they do fit a use case, they can increase readability
at the call site.
 Would part of the system benefit from a custom DSL? For instance, if there is a complex
class (whether your own or third-party) of which you frequently build objects, a type-safe
builder DSL may be a good idea.
 Would my asynchronous code benefit from coroutines? For example, if you’re using
many AsyncTasks, using coroutines can greatly reduce complexity.

Not all these changes are trivial; some can require substantial refactoring. But all are important
considerations to make on the way to a high-quality code base—after all, this is why you would
want to migrate to Kotlin in the first place. My hope is that this checklist helps guide you to a
code base that all developers agree was worth the work for the migration.

Note
Converting any file will delete the original .java file and add a new .kt file. Thus, version
control history for the Java file quickly becomes useless when modifying the Kotlin code.

Summary
This chapter covered the technical and nontechnical aspects of migrating to Kotlin (or a new tool
in general), from implications on build time and code base quality, to getting buy-in and pitching
adoption at your company. This summary recaps the primary steps involved, roughly
in a chronological order.
 Get a good understanding of Kotlin—which you have done with this book.
 Implement a pet project in Kotlin by yourself—you have already created two with guidance
in this book, so try one without step-by-step guidance. Also, if you have an existing Java pet
project, try migrating it to Kotlin.
 Watch for common issues in your company’s code base and map them to Kotlin features
that would help solve those issues.
 Talk to your colleagues about Kotlin and let them know what it can and cannot do. Paint
an accurate picture about its benefits and drawbacks. Be open to discussions and critique to
establish a culture of learning and to get buy-in from other team members.
Summary 357

 Pitch an evaluation of Kotlin at your company, for instance, with a document highlighting
features, benefits, and compatibility with the company’s technology stack.
 If your company wants to adopt Kotlin, decide in advance whether to aim for a full
migration and agree on a migration plan.
 Work on a pet project with your team, ideally evaluating the same technology stack as the
product you want to eventually migrate.
 Migrate a simple feature that is well tested to Kotlin and celebrate the fact that you can
deploy Kotlin to production.
 Use the converter, but expect to adjust the autoconverted code.
 Don’t stop at 90% if you aimed for a full migration, even if the last packages and modules
are harder to migrate, require bigger restructuring, and you could be working on new
features instead. Remember the benefits of full migration.

This is the bird’s-eye view of the main steps involved in the adoption of Kotlin. Keep in mind that
not every developer will be eager to switch to a new programming language, that it introduces
risks on the technical and business level, and that it may in fact not be the best choice for your
company. However, Kotlin can substantially improve developer experience, productivity, code
quality, and eventually product quality. The recommendations in this chapter aim to help you
evaluate which are true in your case.
This page intentionally left blank
A
Further Resources

Official Resources
Kotlin Reference: https://kotlinlang.org/docs/reference/

This is the primary resource for information right from the source. It is well written and briefly
covers all aspects of the language.

Kotlin in Action: https://www.manning.com/books/kotlin-in-action

This book written by Dmitry Jemerov and Svetlana Isakova from JetBrains is a great resource on
the Kotlin language straight from two members of the Kotlin team.

Talking Kotlin: http://talkingkotlin.com/

In this podcast, Hadi Hariri from JetBrains talks to Kotlin developers about anything Kotlin—for
instance DSLs, libraries, Groovy, or Kotlin/Native—for roughly 40 minutes per episode.

Talks from KotlinConf 2017: https://bit.ly/2zSB2fn

The first-ever Kotlin conference was packed with high-quality talks for all levels of experience.
Topics include coroutines, building React apps, interop, data science, and much more. By the
time you read this, there will also be videos from KotlinConf 2018 available on YouTube (provided
above as a shortened URL).

Community
Kotlin Slack Channel: http://slack.kotlinlang.org/

This official Slack channel is packed with experienced Kotlin developers eager to share their
knowledge and answer any questions, whether you’re a beginner or advanced developer.

Kotlin Weekly Newsletter: http://www.kotlinweekly.net/

This weekly newsletter is useful to stay current with the most popular articles written about Kotlin
and to keep exploring new topics and best practices surrounding Kotlin.
360 Appendix A Further Resources

All Community Resources: http://kotlinlang.org/community/

The official Kotlin website maintains an overview of all community resources where you can stay
updated on the platform you prefer.

Functional Programming
Kotlin Arrow: https://arrow-kt.io/

The Arrow library is packed with functional types and abstractions—such as monads, monoids,
and options—to build pure functional apps with Kotlin.

Kotlin DSLs
Anko: https://github.com/Kotlin/anko

The Anko library for Android contains, among other things, a DSL to create layouts program-
matically and replace XML layouts (see Chapter 8, Android App Development with Kotlin:
Nutrilicious).

Kotlin Gradle DSL: https://github.com/gradle/kotlin-dsl

The Kotlin Gradle DSL allows you to write your build scripts with Kotlin instead of Groovy,
enabling autocomplete, code navigation, and other tool support (see Chapter 8).

Kotlin HTML DSL: https://github.com/Kotlin/kotlinx.html

This DSL allows you to write HTML code in a type-safe way in Kotlin. It’s one of the most used
DSL in the Kotlin ecosystem, particularly in combination with Kotlin/JS.

Migrating to Kotlin
Christina Lee’s talk on migration at Pinterest: https://youtu.be/mDpnc45WwlI

In her talk, Christina Lee goes over the challenges she faced when pitching and finally introducing
Kotlin at Pinterest.

Jake Wharton’s document to pitch at Square Inc: https://docs.google.com/document/d/


1ReS3ephjxWA8kZi0YqDbEhCqTt29hG8P44aA9W0DM8/edit?usp=sharing

In this document, Jake Wharton summarizes Kotlin’s benefits and argues why it is worth adopting
for the development of Android apps.

Adoption at Udacity: https://engineering.udacity.com/adopting-kotlin-c12f10fd85d1

In this article, Nate Ebel goes through his team’s experience while migrating to Kotlin, including
the challenges, tips they discovered, and what rules they followed during adoption.
Testing 361

Testing
Spek: https://spekframework.org/

Spek is a specification framework developed by JetBrains that allows you to write tests as client-
readable specifications and avoid misunderstandings when implementing test cases.

KotlinTest: https://github.com/kotlintest/kotlintest

KotlinTest is a testing framework for Kotlin, and another alternative to Spek and JUnit that
supports several testing styles.

MockK: https://mockk.io/

MockK is an extensive mocking library specifically for Kotlin. It allows you to mock final classes,
provides a DSL for mocking, lets you mock coroutines, and much more.

Note
These are only a tiny slice of libraries, frameworks, and resources available for Kotlin. A good
way to discover more is https://kotlin.link/, which curates a comprehensive list of everything
related to Kotlin.
This page intentionally left blank
Glossary

Accessor: Getter or setter; allows access


to a property.
Annotation: Metadata attached to a
certain piece of the code, such as a class
or function.
Annotation processor: Tool that processes
metadata from annotations, often at
compile time.
API (Application Programming
Interface): A set of well-defined inter-
faces for application development, like
reusable building blocks.
ART (Android Runtime): Android run-
time environment that translates
bytecode into native instructions
executed by the Android device.
Block of code: See “Code block.”
Blocking (operation): Operation that
blocks execution of a thread, such as
requesting a lock.
Boilerplate: Typically repeated code
that is necessary to perform a certain
task but does not add to the code’s
actual purpose and may hamper
understandability.
Call site: A place in the code where a
function is called. See also “Declaration
site,” “Use site.”
Callback: Function that is passed to
another entity to be called at the
appropriate time.
364 Changeability (of code)

Changeability (of code): Ease with Dispatch receiver: Class containing the
which developers can make changes to declaration of an extension function.
the code. See also “Maintainability.” Event handler: Callback that is executed
Class: A type with certain data and in case a given event occurs, such as a
capabilities; acts as a blueprint to create button click. See also “Callback.”
objects from. Expression: Piece of code that has a
Code block: Arbitrary number of code value, such as calling a function or
lines encompassed within curly braces accessing a variable in Kotlin.
(in Kotlin). Expressiveness (of code): The ability
Collection: Data structure that holds of code to express the programmer’s
zero or more elements, such as a list intentions and the code’s
or a set. functionality.
Compile-time error: Error that is found Extension receiver: The class extended
and indicated by the compiler, thus can by the extension function.
be fixed before causing a crash at run- File-level declaration: Declaration
time. See also “Runtime error.” directly on the file level, not within
Composite pattern: Design pattern that another declaration. Kotlin allows not
allows creating nested object hierar- only classes and objects on the file level
chies. See also “Strategy pattern.” but also variables and functions.
Composition: Containment of one First-class citizen: Language construct
entity in another. Often used to delegate that allows common operations such as
certain tasks to the contained entity. assigning it to a variable, passing it in
See also “Delegation.” as an argument, or returning it from a
Conciseness (of code): The level of function.
textual brevity of the code. See also Function: Piece of code that may accept
“Syntax.” inputs (as parameters), performs the
Constructor: Special function that operations defined in its function body,
allows creating an object from a class. and may produce an output (as return
See also “Class.” value).

D8: Android dexer that enables faster Generator: Code construct that emits a
compilation than its predecessor DX. sequence of values, yielding them one
at a time.
Declaration: Introduces a new identifier
and what it represents, for instance, a I/O: Input and output, for instance,
function or a variable of a certain type. from a database, the network, or files.
See also “Initialization.” Immutability: See “Mutability.”
Declaration site: Place in the code Inheritance: The ability to inherit data
where an identifier is declared. See also and capabilities from parent classes to
“Use site,” “Call site.” child classes.
Delegation: Referring to a different Initialization: Assigns an initial value to
entity or implementation to implement a variable, oftentimes combined with its
functionality. declaration. See also “Declaration.”
Strategy pattern 365

JVM (Java Virtual Machine): Runtime Receiver class (of an extension


environment for Java bytecode, whether function): See “Extension receiver.”
compiled from Kotlin, Java, or another Refactoring: Improving the internal
JVM language. It abstracts from the structure of code without changing its
operating systems (OS) so that programs external behavior.
can run on any OS.
Reliability (of code): The level to which
Keyword: Reserved word that carries a code behaves as expected and without
special meaning, such as while, try, or failures.
null. Soft keywords can be used as iden-
tifies, hard keywords cannot. See also Runtime error: Error that occurs only
“Modifier.” at runtime and can cause the program
or app to crash. See also “Compile-time
Maintainability (of code): Ease with error.”
which developers can change, improve,
and extend the code. Scope: A variable’s scope is the part of
the code in which the variable is visible
Memory leak: When memory cannot (accessible).
release data even though it is no longer
needed by the program, for instance, Semantics: Meaning of the syntactical
because there are still unused references constructs of a language (such as val,
to the data. indicating a variable declaration that is
not reassignable). See also “Syntax.”
Method: Function that is inside a class
or object (not on the file level). See also Shared (mutable) state: A program state
“Function,” “File-level declaration.” shared by multiple concurrent units
(threads or coroutines). Can lead to syn-
Modifier: Reserved word with special chronization problems if it is mutable.
meaning in a declaration, such as open, See also “Mutual exclusion.”
private, or suspend. Can be used as
identifier in other contexts. See also Singleton: A design pattern that allows
“Declaration.” at most (or exactly) one instance of a
class at runtime.
Mutability: The ability of an identifier
or data to change after initialization. Smart cast: Automatic type cast by the
Kotlin compiler when type constraints
Mutual exclusion: Restricts access and execution context permit. See also
to a critical code section to a single “Type cast.”
concurrent unit (typically a thread, or
a coroutine in Kotlin). See also “Shared Statement: Piece of code that defines
(mutable) state.” actions to be done but does not have a
value.
Nullability: The ability of a variable or
data to be assigned null as a value. Static typing: Expression types are well
defined before compile time, only based
Operator: Shorthand syntax for func- on the source code, to support type
tion calls in Kotlin, such as +, -, in, or %. safety.
Readability (of code): Ease with which Strategy pattern: Design pattern in
developers can read and understand which the to-be-used algorithm is chosen
the code. at runtime. See also “Composite pattern.”
366 Syntax

Syntax: Textual rules that make up a classes, and functional types.


valid program. See also “Semantics.” See also “Class.”
Top-level declaration: See “File-level Type cast: Transforming a variable or
declaration.” data of a certain type into another type.
Type: The capabilities of a variable Use site: A place in the code where
or data in general. Includes classes, an identifier is used. See also
concrete instantiations of generic “Declaration site,” “Call site.”
Index

Symbols
& (ampersand), 15
-> (arrow), 16
* (asterisk)
multiplication operator (*), 28
timesAssign operator (*=), 28
` (backtick), 130
[ ] (brackets), 28
: (colon), 39
= (equal sign), 22
! (exclamation mark)
not (!) operator, 15
unsafe call operator (!!), 31, 355
/ (forward slash)
divAssign operator (/=), 28
division operator (/), 28
- (minus sign)
dec operator (--), 28
minusAssign operator (-=), 28
subtraction operator (-), 28
% (percent sign)
rem operator (%), 28
remAssign operator (%=), 28
. (period), 28
| (pipe symbol), 15
+ (plus sign)
inc operator (++), 28
plusAssign operator (+=), 28
368 ? (question mark)

? (question mark) algebraic data types, 100


elvis operator (?:), 30–31 ALGOL W, 29
safe call operator (?), 29–30 aliases, type, 107
allprojects block (Gradle Kotlin DSL), 337
A also function, 58–59
abstract classes, 87–88 Amount class, 307–311
abstract keyword, 87 ampersand (&), 15
accessing collections, 47 and (&&) operator, 15
actionable data (Nutrilicious app), 307–311 Android, 125
activities, binding coroutines to, 177–179 android block (Gradle Kotlin DSL), 338–339
actor function, 187–188 Android KTX, 7, 230
actor model for concurrent programming, Android layouts
155, 186–192 Anko
conflated channels, 188–189 Anko dependencies, 329–330
custom message types, 191–192 custom views, 334–335
history of, 192 layout parameters, 330–331
multiple actors on same channel, migrating Kudoo’s AddTodoActivity
190–191 to, 332
multiple channels, 189–190 modularizing, 333
producers, 191 previewing, 331
ReceiveChannel, 188 simple layout example, 330
rendezvous channels, 190 XML layouts versus, 335
SendChannel, 187–188 creating programmatically, 328–329
simple example, 186–187 overview of, 328
unlimited channels, 189 Android PacKage (APK), 258
adapters, RecyclerView Android Runtime (ART), 5–6
Kudoo app, 215–219 Android SDK (software development kit),
Nutrilicious app, 246–248 124
addition (+) operator, 28 Android Search Interface, 265
address function, 322, 323 Android Studio
AddressBuilder class, 322 autocompletion, 28
addresses, adding to users compiled bytecode, viewing, 134–135
multiple addresses, 323–325 decompiled Java code, viewing, 134–135
single addresses, 319–320 Device File Explorer, 227
addresses block, 323–325 overview of, 7, 8, 205

AddTodoActivity, 233–237 Sample Data Directory, 258


creating programmatically, 328–329 Android Virtual Device (AVD), 212
migrating to Anko layouts, 332
apps 369

android-apt plugin, 208–209 Collections. See collections


AndroidExtensionsExtension, 340 Search API, 255–256, 258–260
Anko library, 6 USDA Nutrition API, fetching data from
dependencies, 329–330 API requests, performing, 256–257
layouts Gradle dependencies for, 250–251
custom views, 334–335 Retrofit interface, 251–256
migrating Kudoo’s AddTodoActivity AppDatabase class, 223–225, 282
to, 332 apply function, 54–55, 329
modularizing, 333 apps
parameters, 330–331 Kudoo
previewing, 331 AddTodoActivity, 233–237
simple example, 330 to-do items, adding, 233–237
XML layouts versus, 335 to-do items, checking off, 237–239
ankoView function, 334–335 event handlers, 237–239
annotation processors, 208–209 finished app, 210
annotations migrating to Anko layouts, 332
annotation processors, 208–209 project setup, 210–212
@Database, 223–224 RecyclerView, 212–221
@DslMarker, 325–326 Room database, 221–233
@Entity, 222 Nutrilicious
@GET, 255 data mapping, 257–262
@JvmField, 135–136, 150 DetailsActivity, 293–301
@JvmName, 134, 137, 150 empty search results, indicating,
@JvmOverloads, 141, 150 311–312
@JvmStatic, 140, 150 Favorites fragment, 276–280
@JvmSuppressWildcards, 147–148 finished app, 241
nullability, 128–129 food details, storing in database,
@Query, 255 302–306

@RestrictsSuspension, 185 migrating to Gradle Kotlin DSL,


336–343
@SerializedName, 261
progress indicator, 312–313
@Throws, 145–146, 149
project setup, 242–243
@TypeConverter, 303
RDIs (recommended daily intake),
@TypeConverters, 303
adding, 307–311
@UserDsl, 325
RecyclerView, 243–250, 261–262
Any type, 88 Room database, 280–288
APIs (application programming interfaces) search interface, 265–268
Android KTX, 7 SearchFragment, 268–275
asynchronous APIs, wrapping, 195–197 SearchViewModel class, 262–264
370 apps

USDA Food Reports API, fetching B


data from, 288–293
backing fields, 72
USDA Nutrition API, fetching data
from, 250–257 backtick (`), 130
Archivable interface, 86 binding coroutines, 177–179
arguments, 22 bindUi function, 309
arithmetic operators, table of, 28 Bloch, Joshua, 88
Array type, 127 blocks, 336
arrayListOf, 46 Boolean data type, 13
arrays Bossidy, Larry, 69
creating, 128 bounded type parameters, 118–119
invariance of, 111 Buck, 7
arrow (->), 16 build scripts
ART (Android Runtime), 5–6 Gradle Kotlin DSL, migration to, 335–
336
as operator, 90
benefits of, 342
assigning lambda expressions, 40
buildSrc directory, 340–342
assignment operators, 28
drawbacks of, 342–343
associate function, 49
Gradle settings, 336
associating collections, 49
module build script, 338–340
asterisk (*)
root build script, 336–338
multiplication operator (*), 28
overview of, 205–207
timesAssign operator (*=), 28
builders, coroutine
async keyword, 157, 169–172
actor, 186–191
asynchrony
async, 169–172
with async-await pattern, 157
contexts, 172
asynchronous APIs, wrapping,
195–197 accessing elements of, 181

with callbacks, 155–156 coroutine dispatchers, 172–177

with coroutines, 158–159, 160 jobs, 177–181

with futures, 156–157 future, 182

overview of, 152–153, 155–157 immutability through, 320–322

autoconverted code, adjusting, launch, 164–169


355–356 overview of, 163
auto-generated Gradle configuration, parameters of, 181–183
205–207 runblocking, 163–164
AutoValue, 348 withContext, 176
AVD (Android Virtual Device), 212 build.gradle file, 205–207
await keyword, 157 buildIterator function, 185
channels 371

BuildPlugins object, 342 vararg methods, calling,


buildscript block (Gradle Kotlin DSL), 130–131
336–337 Kotlin code from Java, 133. See also
buildSequence function, 184 visibilities

buildSrc directory, 340–342 data classes, 142–143

business, Kotlin for extensions, 137–138

advantages of, 7–8 file-level declarations, 136–137

companies incorporating Kotlin, 8 Java-friendly Kotlin code, writing,


149–150
platform compatibility, 9
method overloading, 141
Butter Knife, 348
properties, calling, 133–135
buy-in for migration, gaining, 347–348
properties, exposing as fields,
Byte data type, 13 135–136
sealed classes, 142–143
C static fields, 138–139
C language, 9 static methods, 140
C++316 methods, 80–81
callback hell, 155–156 operators, 27
callbacks, 155–156 casting types
calling ClassCastException, 90
functions, 22 nullable types, 90
chaining calls, 52–53 smart casts, 30, 90–91
extension functions, 25 catch keyword, 33–35
higher-order functions, 42 catching exceptions, 33–35
infix functions, 27 Ceylon, 4
lambas as arguments to, 42 chaining function calls, 52–53
getters/setters, 124–125 channels
Java code from Kotlin conflated, 188–189
considerations for, 132–133 multiple, 189–190
getters, calling, 124–125 multiple actors on same channel,
identifiers, escaping, 130 190–191
Kotlin-friendly Java code, writing, overview of, 186–192
149–150 producers, 191
nullability, 125–129 ReceiveChannel, 188
operators, 131 rendezvous, 190
overview of, 123–124 SendChannel, 187–188
SAM (single abstract method) types, simple example, 186–187
131–132 unlimited, 189
setters, calling, 124–125
372 Char data type

Char data type, 13 inner, 82


checked exceptions, 35–36 KClass, 143–144
child coroutines, 177–178 methods
class keyword, 69 calling, 80–81
ClassCastException, 90 declaring, 80–81
classes dynamic dispatch, 80–81
abstract, 87–88 extension, 81
AddressBuilder, 322 getters, 71
Amount, 307–311 overriding, 80–81
AppDatabase, 223–225, 282 setters, 71
constructors nested, 82
primary, 70, 82–83 Nutrient, 308
secondary, 84 NutrientListConverter, 302–303
data classes NutrientTypeConverter, 303
calling from Java, 142–143 open, 88–89
declaring, 94–95 polymorphism, 80–81
inheritance, 96 properties
declaring, 69 adding, 70–71
top-level declarations, 93–94 backing fields, 72
visibility modifiers, 92–93 delegated, 74–79
delegated implementations, 79–80 fields compared to, 71
DetailsDto, 291 late-initialized, 72–73
DetailsRepository, 305–306 lazy, 75–76
DetailsViewModel, 298–300, 306 observable, 76–77
domain classes, mapping data to sealed
DTO-to-model mapping, 260–262 calling from Java, 142–143
JSON-to-DTO mapping, 258–260 declaring, 98–100
overview of, 257–258 SearchListAdapter, 246–248, 283–284
enum, 96–98 SearchViewModel, 262–264
FavoritesViewModel, 282–283, 286–287 TodoItem, 222
Food, 246, 280–281 TodoItemDao, 222–223
FoodDetails, 291–292, 302 types versus, 110
FragmentActivity, 230 User
generic, 106–107 adding addresses to, 319–320,
immutable, 320–321
323–325

inheritance
declaring, 319
UserBuilder, 321
overriding rules for, 89
ViewHolder, 216–217
overview of, 84–85
constructors 373

ViewModel concise code, 4


Kudoo app, 227–230 concurrency. See also coroutines
Nutrilicious app, 262–264 actor model for, 186–192
class-local extensions, calling, 138 conflated channels, 188–189
Clojure, 4 custom message types, 191–192
closed-by-default principle, 85, 88 history of, 192
ClosedSendChannelException, 187 multiple actors on same channel,
COBOL, 316 190–191
multiple channels, 189–190
code base, integrating Kotlin into, 351
producers, 191
pet projects, 353–354
ReceiveChannel, 188
production code, 352–353
rendezvous channels, 190
test code, 351–352
SendChannel, 187–188
collections
simple example, 186–187
accessing, 47
unlimited channels, 189
associating, 49
challenges of, 153–155
editing, 47
generators, 184–185
filtering, 47–48
overview of, 151–153
folding, 51–52
solutions to, 155–157
grouping, 49
transformation between styles of, 193
helper functions for, 46
instantiating, 46
concurrent execution, 152–153

Kotlin versus Java, 45–46 conditional expressions

mapping, 48 if keyword, 15–19

minimum, maximum, and sum when keyword, 15–19


calculations, 49–50 configuration. See also Kudoo app;
sorting, 50–51 Nutrilicious app

variance of, 111–112 AVD (Android Virtual Device), 212

Collections API. See collections Grade

colon (:), 39 annotation processors, 208–209


auto-generated configuration,
combining higher-order functions, 59–60
205–207
companion keyword, 103–105
dependencies, adding, 207–208
companion objects, 103–105
conflated channels, 188–189
compiled bytecode, viewing, 134–135
const keyword, 135
compile-time polymorphism, 108
constructor keyword, 83
CompletableFuture, 152, 182–183
constructors
complex objects, building with DSLs
primary, 70, 82–83
(domain-specific languages), 318–320
secondary, 84
374 contains operator

contains operator, 28 actor, 186–191


contexts, coroutine, 172 async, 169–172
accessing elements of, 181 contexts, 172–181
coroutine dispatchers, 172–177 future, 182
jobs, 177–181 immutability through, 320–322
Continuation Passing Style (CPS), 156 launch, 164–169
continuations, 156 overview of, 163
contravariance, 112 parameters of, 181–183
conversions, SAM (single abstract method), runblocking, 163–164
131–132 withContext, 176
Convert Java File to Kotlin File command child, 177–178
(Code menu), 209, 355 contexts, 172
converter, Java-to-Kotlin, 8, 355 accessing elements of, 181
Conway, Melvin E.192 coroutine dispatchers, 172–177
cooperative multitasking, 157–158 jobs, 177–181
coroutine builders debugging, 193–195
actor, 186–191 generator implementation with,
async, 169–172 184–185
contexts, 172–181 Gradle dependencies for, 256
future, 182 history of, 192
immutability through, 320–322 interoperability with Java,
launch, 164–169 197–198

overview of, 163 lazy start, 182

parameters of, 181–183 overview of, 157–158

runblocking, 163–164 parameters of, 181–183

withContext, 176 setup for, 158

coroutine dispatchers (Nutrilicious app), suspending functions, 159–163


283 wrappers for, 182
CoroutineContext, 172 CoroutineStart, 181–182
CoroutineDispatcher, 172–177 covariance, 110–112
Job object, 177–181 Covey, Stephen, 123
CoroutineDispatcher, 172–177 CPS (Continuation Passing Style), 156
CoroutineExceptionHandler, 180 CPU-bound operations, 165
coroutines crossinline keyword, 44
asynchrony with, 158–159, 160, custom message types, actors with,
195–197 191–192
binding to activities, 177–179 custom views, Anko layouts,
builders 334–335
declarations 375

D platform types, 126–128


table of, 13–14
Dalvik virtual machine, 5–6
type inference, 14–15
DAOs (data access objects)
variance
FavoritesDao interface, 281
bounded type parameters, 118–119
TodoItemDao class, 222–223
contravariance, 112
data classes
covariance, 110–112
calling from Java, 142–143
declaration-site, 113–116
declaring, 94–95
star projections, 119–121
inheritance, 96
use-site, 116–118
data keyword, 94–95
@Database annotation, 223–224
data mapping (Nutrilicious app)
databases, 6
DTOs to models, 260–262
Kudoo Room database
JSON to DTOs, 258–260
AppDatabase class, 223–225
overview of, 257–258
Gradle dependencies for, 221–222,
data transfer objects. See DTOs (data 225–226
transfer objects)
LiveData class, 230–233
data types. See also nullability
MainActivity, 226–227
algebraic, 100
TodoItem class, 222
aliases, 107
TodoItemDao class, 222–223
Any, 88
ViewModel class, 227–230
casting
Nutrilicious Room database
ClassCastException, 90
AppDatabase class, 282
nullable types, 90
coroutine dispatchers, 283
smart casts, 30, 90–91
FavoritesDao interface, 281
checking, 89
FavoritesViewModel class,
classes versus, 110 282–283, 286–287
function types, 39 Food class, 280–281
generic type parameters, 105–106 SearchFragment, 287–288
mapped, 125 SearchListAdapter class, 283–284
mapping, 14 storing food details in, 302–306
nullable toggleFavorite function, 285
casting, 90 deadlock, 153
elvis operator (?:), 30–31 debugging coroutines, 193–195
overview of, 29
dec operator (--), 28
safe call operator (?), 29–30
declarations
unsafe call operator (!!), 31, 355
classes, 69
parameters, 40–41, 105–106
data, 94–95
376 declarations

enum, 96–97 design patterns, Strategy, 39


generic, 106–107 DetailsActivity
sealed, 98–100 click handler, 297
top-level declarations, 93–94 data display, 300–301
User, 319 DetailsViewModel class, 298–300
visibility modifiers, 92–93 FOOD_ID_EXTRA identifier, 298
file-level, 136–137 item click listener, 296–297
functions, 21 layout, 293–296
extension, 25 navigation, 301
generic, 107–108 string resources for headline, 296
infix, 27 DetailsDto class, 291
main, 22–23 DetailsRepository class, 305–306
suspending, 159 DetailsViewModel class, 298–300, 306
generators, 184 development of Kotlin, 3–4
getters/setters, 71 Device File Explorer, 227
methods, 80–81 DiffUtil, 233
objects, 103–105 dimension resources, 214
operators, 27 dining philosophers analogy, 153–155
variables, 12–13 direct style, 156
mutable, 12 directories
read-only, 12 buildSrc, 340–342
declaration-site variance, 113–116, Sample Data Directory, 258
146–148
dispatch receivers, 81
decompiled Java code, viewing, 134–135
dispatchers, coroutine, 172–177, 283
deep nesting, 323–325
division (/) operator, 28
DefaultDispatcher object, 165
to-do list app. See Kudoo app
delegated implementations, 79–80
domain classes, mapping data to
delegated properties
DTO-to-model mapping, 260–262
implementation of, 74–75
JSON-to-DTO mapping, 258–260
lazy properties, 75–76
overview of, 257–258
maps, 78–79
domain-specific languages. See DSLs
observable properties, 76–77 (domain-specific languages)
syntax of, 74 Double data type, 13
dependencies doubleArrayOf method, 128
adding, 207–208 do-while loops, 19
Anko, 329–330 drop function, 64–65
Descartes, René, 37 @DslMarker annotation, 325–326
exception handling 377

DSLs (domain-specific languages) editing collections, 47


benefits of, 316–317 Effective Java (Bloch), 88
creating Elixir, 186
complex objects, building, 318–320 else keyword, 15
deep nesting, 323–325 else-if keyword, 15
@DslMarker annotation, 325–326 elvis operator (?:), 30–31
immutability through builders, embedded DSLs (domain-specific
320–322 languages), 316
simple objects, building, 318 empty search results, indicating, 311–312
definition of, 315–316 @Entity annotation, 222
drawbacks of, 317–318 enum classes, 96–98, 309
embedded, 316 enumerations, 96–98
Gradle Kotlin DSL, migration to, equal sign (=)
335–336
assignment operators, 28
benefits of, 342
referential equality operator (===), 32
buildSrc directory, 340–342
single-expression functions, 22
drawbacks of, 342–343
structural equality operator (==), 32
Gradle settings, 336
equality
module build script, 338–340
checking, 32
root build script, 336–338
floating point, 32–33
history of, 315
Erlang, 186
language features, 326–328
escaping Java identifiers, 130
layouts, 328
evaluation, eager, 62, 65
creating programmatically,
328–329 evaluation, lazy

creating with Anko, 329–335 concept of, 62

performance of, 327–328 definition of, 38

readability of, 323–327 lazy sequences, 63–66

DTOs (data transfer objects) creating, 63–64

DetailsDto, 291 drop function, 64–65

mapping JSON data to, 258–260 performance of, 65–66

mapping to models, 260–262 take function, 64–65

wrappers for, 290 event handlers (Kudoo app), 237–239

dynamic dispatch, 80–81 Evernote, 8


exception handling
E checked exceptions, 35–36
ClassCastException, 90
eager evaluation, 62, 65
ClosedSendChannelException, 187
Eclipse, 7
378 exception handling

CoroutineExceptionHandler, 180 extension methods, 81


Java interoperability, 145–146 extension receivers, 81
NullPointerException, 4, 8, 29 extensions
principles of, 33–35 AndroidExtensionsExtension, 340
suspending functions, 162 calling from Java, 137–138
TimeoutCancellationException, 177 experimental, enabling in Gradle Kotlin
unchecked exceptions, 35–36 DSL, 340

exclamation mark (!)


not (!) operator, 15 F
unsafe call operator (!!), 31, 355 Favorites fragment, 276–280
execution, concurrent, 152–153 FavoritesDao interface, 281
experimental extensions, enabling in Gradle FavoritesViewModel class, 282–283,
Kotlin DSL, 340 286–287
expressions fetchDetailsFromApi function, 306
conditional fetching data
if keyword, 15–19 from USDA Food Reports API,
when keyword, 15–19 288–293
eager evaluation, 62, 65 from USDA Nutrition API
lambda API requests, performing, 256–257
as arguments to higher-order Gradle dependencies for, 250–251
functions, 42 Retrofit interface, 251–256
assigning, 40 fib function, 21–22
defining, 40 fields, 71
definition of, 37 exposing properties as, 135–136
implicit arguments, 41 static, calling from Java, 138–139
with receivers, 60–61 file-level declarations, 136–137
type inference, 40–41 filter function, 47–48
lazy evaluation filtering collections, 47–48
concept of, 62 Find Action command, 207
definition of, 38 FindBugs, 125
lazy sequences, 63–66 findViewById function, 217–218
object, 101–102 first-order functions, 39
extension functions Float data type, 13
Android KTX, 230 Floating Point Arithmetic, 32
creating, 25 floating point equality, 32–33
importing, 26 flow control
scope, 26 conditional expressions
static resolving of, 25–26
functional programming 379

if keyword, 15–19 setUpSearchRecyclerView function,


when keyword, 15–19 271

loops storing in activity’s state, 273

do-while, 19 swipe refresh, 275

for, 20–21 SwipeRefreshLayout,


271–272
while, 19
updateListFor function, 271
fold function, 51–52
transactions, 272
folding collections, 51–52
frameworks for testing, 353
food and nutrition app. See Nutrilicious app
fromString method, 309
Food class, 246, 280–281
full migration, 350–351
food details, storing in database,
302–306 fun keyword, 21, 25, 130

FOOD_ID_EXTRA identifier, 298 functional programming. See also


functions
FoodDetails class, 291–292, 302
benefits of, 38–39
for loops, 20–21
collections
Ford, Henry, 205
accessing, 47
Fortran, 316
associating, 49
forward slash (/)
editing, 47
divAssign operator (/=), 28
filtering, 47–48
division operator (/), 28
folding, 51–52
FragmentActivity class, 230
grouping, 49
fragments
helper functions for, 46
Favorites, 276–280
instantiating, 46
FragmentActivity class, 230
Kotlin versus Java, 45–46
SearchFragment
mapping, 48
adding to UI, 273
minimum, maximum, and sum
database connection, 287–288 calculations, 49–50
fragment transactions, 272 sorting, 50–51
getViewModel extension, 270–271 variance of, 111–112
layout for, 268–269 lambda expressions
MainActivity, 269 assigning, 40
methods, 269–270 defining, 40
onCreate method, 274 definition of, 37
properties, 272 implicit arguments, 41
recoverOrBuildSearchFragment type inference, 40–41
method, 274
lazy evaluation, 38
restoring, 274
purpose of, 37–38
search intents, 275
380 functions

functions. See also lambda expressions; names


methods; operators calling, 42
actor, 187–188 combining, 59–60
address, 322, 323 defining, 41
also, 58–59 inlining, 42–43
ankoView, 334–335 lambas as arguments to, 42
apply, 54–55, 329 non-local returns, 44–45
arguments, 22 suspending functions and,
arrayListOf, 46 162–163
associate, 49 infix, 26–27, 323–327
bindUi, 309 inline, 145
buildIterator, 185 lazy evaluation
buildSequence, 184 concept of, 62
callbacks, 155–156 lazy sequences, 63–66
calling, 22, 52–53 let, 53–54
chaining calls to, 52–53 linkedMapOf, 46
declaring, 21 linkedSetOf, 46
drop, 64–65 listOf, 46
extension lparams, 330–331
creating, 25 main, 22–23
importing, 26 makeSection, 309
scope, 26 map, 48
static resolving of, 25–26 mapOf, 46
fetchDetailsFromApi, 306 maxBy, 49–50
fib, 21–22 minBy, 49–50
filter, 47–48 mutableListOf, 46
findViewById, 217–218 mutableMapOf, 46
first-order, 39 mutableSetOf, 46
fold, 51–52 overloading, 24–25
function types, 39 parameters, 23–24
generic, 107–108 plus, 180
getDetails, 300–301, 306 pointers to, 39
getPercentOfRdi, 310 printAll, 119–121
getViewModel, 264 receive, 187
groupBy, 49 reduce, 52
hashMapOf, 46 reduceRight, 52
hashSetOf, 46 renderNutrient, 301, 310
higher-order, 37. See also specific function run, 56–58
Gradle Kotlin DSL, migration to 381

send, 187 functions, 107–108


sendEmail, 24 reification, 108–109
setLoading, 313 type parameters, 105–106
setOf, 46 @GET annotation, 255
setUpSearchRecyclerView, 271 get method, 71
signatures, 21–22, 39, 144–145, 159 get operator, 28
single-expression, 22 getDetails function, 300–301, 306
sorted, 50 getFoods method, 257
sortedArray, 50 getItemCount method, 216, 218–219, 246
sortedBy, 50 getPercentOfRdi function, 310
sortedDescending, 50 getters
sortedMapOf, 46 calling, 124–125
sortedSetOf, 46 declaring, 71
sortedWith, 50 getViewModel extension, 270–271
sumBy, 49–50 getViewModel function, 264
suspendCoroutine, 183 Git, 212
suspending, 157, 159–163, 164, 198–200 GitHub, 158, 212, 242, 307
take, 64–65 .gitignore file, 212
toast, 330 Go, 186
toggleFavorite, 285 Google, support for Kotlin, 5
toSortedMap, 50 Gradle dependencies
updateListFor, 271, 275, 311 Kudoo app, 221–222, 225–226
updateUiWith, 313 Nutrilicious app
use, 59 architectural components, 262
user, 318 Kotlin coroutines, 256
verticalLayout, 330 Moshi, 258
with, 55–56 network and API access, 250–251
withContext, 176
Gradle Kotlin DSL, migration to, 7, 335–336
withTimeout, 176
benefits of, 342
yield, 184
buildSrc directory, 340–342
future coroutine builder, 182 configuration
futures, 152, 156, 255 annotation processors, 208–209
auto-generated, 205–207
G dependencies, adding, 207–208
generators, 184–185 drawbacks of, 342–343
generics Gradle settings, 336
benefits of, 105 Kudoo app, 221–222, 225–226
classes, 106–107 module build script
382 Gradle Kotlin DSL, migration to

android block, 338–339 variables, 13


dependencies, 339–340 implementations, delegated, 79–80
experimental features, 340 implicit arguments, lambda expressions, 41
plugins block, 336–338 importing extension functions, 26
root build script in operator, 28
allprojects block, 337 inc operator (++), 28
buildscript block, 336–337 indexed access operator, 47
delete task, 337–338 inference, type, 14–15
support for, 346 infix functions, 26–27, 323–327
Gradle Profiler, 353 infix keyword, 26–27
groupBy function, 49 inheritance
grouping collections, 49 abstract classes, 87–88
Gson, 346 closed-by-default principle, 85, 88
data classes, 96
H interfaces, 85–86
hashMapOf function, 46 open classes, 88–89
hashSetOf function, 46 overriding rules for, 89
helper functions, for collections, 46 overview of, 84–85

Hewitt, Carl, 186, 192 initializing objects, 55


higher-order functions. See also specific inline functions
function names calling from Java, 145
calling, 42 higher-order functions, 42–43
combining, 59–60 inline keyword, 42–43, 145
defining, 41 inner classes, 82
inlining, 42–43 instantiating collections, 46
lambas as arguments to, 42 Int data type, 13
non-local returns, 44–45 intArrayOf method, 128
overview of, 37 integrating Kotlin into code base, 351
suspending functions and, 162–163 pet projects, 353–354
Hoare, Tony, 29 production code, 352–353
HTTPLoggingInterceptor, 253 test code, 351–352
IntelliJ IDEA, 7, 8
I autocompletion, 28
identifiers, escaping, 130 compiled bytecode, viewing, 134–135
if keyword, 15–19 decompiled Java code, viewing,
134–135
immutability
Interceptor interface, 254
data classes, 320–321
interfaces
through builders, 320–322
Java interoperability 383

Archivable, 86 variance, 115, 116


declaring, 92–93 visibilities, mapping to Java
FavoritesDao, 281 exception handling, 145–146
inheritance, 85–86 inline functions, 145
Interceptor, 254 KClass, 143–144
internal keyword, 92, 94 Nothing type, 148
Internet access, enabling, 256 overview of, 143
interoperability with Java signature clashes, 144–145
best practices for, 149–150 variant types, 146–148
Collections API, 45–46 interpolation, string, 20
CompletableFuture, 182–183 iOS, 3
coroutines, 197–198 is operator, 89
Java code, calling from Kotlin it keyword, 61
considerations for, 132–133 item layout, Nutrilicious app, 244–246
getters, 124–125 iterator method, 124
identifiers, escaping, 130
Kotlin-friendly Java code, writing, J
149–150
Java code, calling from Kotlin
nullability, 125–129
considerations for, 132–133
operators, 131
getters, 124–125
overview of, 4, 123–124
identifiers, escaping, 130
SAM (single abstract method) types,
131–132 nullability

vararg methods, 130–131 annotations for, 128–129

Java-to-Kotlin converter, 209 nullables and mapped types, 125

Kotlin code, calling from Java, 133 overview of, 125

data classes, 142–143 platform types, 126–128

extensions, 137–138 operators, 131

file-level declarations, 136–137 overview of, 4, 123–124

Java-friendly Kotlin code, writing, SAM (single abstract method) types,


149–150 131–132

method overloading, 141 vararg methods, 130–131

properties, calling, 133–135 Java interoperability

properties, exposing as fields, best practices for, 149–150


135–136 Collections API, 45–46
sealed classes, 142–143 CompletableFuture, 182–183
static fields, 138–139 coroutines, 197–198
static methods, 140 Java code, calling from Kotlin
384 Java interoperability

considerations for, 132–133 Java on Android, 5–6


getters, calling, 124–125 Java Streams, 6
identifiers, escaping, 130 JavaFX, 6, 182
Kotlin-friendly Java code, writing, JavaScript, 3
149–150 Kotlin/JS, 9
nullability, 125–129 Kotlin’s advantages over, 9
operators, 131 Java-to-Kotlin converter, 8, 209, 355
overview of, 123–124 JetBrains, 3–4, 7, 125
SAM (single abstract method) types,
Job object, 177–181
131–132
jobs, coroutine, 177–181
setters, calling, 124–125
JSON data, mapping to DTOs (data transfer
vararg methods, calling, 130–131
objects), 258–260
Java-to-Kotlin converter, 8, 209, 355
JSR 305, 125
Kotlin code, calling from Java, 133
JUnit, 124, 352
data classes, 142–143
JVM (Java virtual machine), 3, 5, 9
extensions, 137–138
@JvmField annotation, 135–136, 150
file-level declarations, 136–137
@JvmName annotation, 134, 137, 150
Java-friendly Kotlin code, writing,
@JvmOverloads annotation, 141, 150
149–150
method overloading, 141 @JvmStatic annotation, 140, 150

properties, calling, 133–135 @JvmSuppressWildcards annotation,


147–148
properties, exposing as fields,
135–136
sealed classes, 142–143 K
static fields, 138–139 kapt plugin, 208–209
static methods, 140 kaptAndroidTest, 209
Kotlin’s advantages over Java, 6, 9 kaptTest, 209
overview of, 4 KClass, 143–144
streams, 62 Keller, Gary, 151
variance, 115, 116 keywords. See also coroutine builders;
visibilities, mapping to Java functions; methods
exception handling, 145–146 abstract, 87
inline functions, 145 async, 157, 169–172
KClass, 143–144 await, 157
Nothing type, 148 catch, 33–35
overview of, 143 class, 69
signature clashes, 144–145 companion, 103–105
variant types, 146–148 const, 135
lambda expressions 385

constructor, 83 Kotlin/JVM, 9
crossinline, 44 Kotlin/Native, 9
data, 94–95 .kts file extension, 11–12
do-while, 19 Kudoo app
else, 15 AddTodoActivity, 233–237
else-if, 15 creating programmatically, 328–329
for, 20–21 migrating to Anko layouts, 332
fun, 21, 25, 130 to-do items, adding, 233–237
if, 15–19 to-do items, checking off, 237–239
infix, 26–27 event handlers, 237–239
inline, 42–43, 145 finished app, 210
internal, 92, 94 project setup, 210–212
it, 61 RecyclerView
lateinit, 73 adapter, 215–219
launch, 164–169 layouts, 213–214
object, 101–102, 130 MainActivity, 220–221
open, 87 model, 215
override, 86, 104–105 overview of, 212–213
private, 92, 94 Room database
protected, 92 AppDatabase class, 223–225
public, 92, 94 Gradle dependencies for, 221–222,
reified, 109 225–226
runblocking, 163–164 LiveData class, 230–233
sealed, 98–100 MainActivity, 226–227
static, 104 TodoItem class, 222
super, 87 TodoItemDao class, 222–223
suspend, 159–163, 198–200 ViewModel class, 227–230
this, 61
throw, 33–35 L
try, 33–35 lambda expressions, 5–6
val, 12, 71, 83, 130 as arguments to higher-order functions,
var, 12–13, 71, 83, 130 42
when, 15–19, 99–100 assigning, 40
where, 118–119 defining, 40
while, 19 definition of, 37
knowledge sharing, 348–349 implicit arguments, 41

Kotlin/JS, 9 with receivers, 60–61


386 lambda expressions

suspending, 164 take function, 64–65


type inference, 40–41 lazy properties, 75–76
lateinit keyword, 73 lazy sequences
lateinit property, 136, 139 creating, 63–64
late-initialized properties, 72–73 drop function, 64–65
LaTeX, 316 overview of, 63–66
launch coroutine builder, 164–169 performance of, 65–66
layouts take function, 64–65
Anko leading migration to Kotlin
Anko dependencies, 329–330 buy-in, gaining, 347–348
custom views, 334–335 knowledge sharing, 348–349
layout parameters, 330–331 Lee, Christina, 347
migrating Kudoo’s AddTodoActivity let function, 53–54
to, 332 libraries. See also interoperability with Java
modularizing, 333 Anko. See Anko library
previewing, 331 AutoValue, 348
simple layout example, 330 Buck, 7
XML layouts versus, 335 Butter Knife, 348
creating programmatically, 328–329 Gradle, 7
Nutrilicious app Gson, 346
DetailsActivity, 293–296 Lombok, 348
Favorites fragment, 276 Mockito, 7
item layout, 244–246 Moshi, 346
MainActivity, 243–244 Retrofit, 7
SwipeRefreshLayout, 271–272 Retrolambda, 348
overview of, 328 linkedMapOf function, 46
RecyclerView, 213–214 linkedSetOf function, 46
SearchFragment, 268–269 List type, 127
XML, 335
listOf function, 46
lazy evaluation LiveData class, 230–233
concept of, 62
locks, 154
definition of, 38
Lombok, 348
lazy sequences
Long data type, 13
creating, 63–64
long[] type, 127
drop function, 64–65
LongArray! type, 127
overview of, 63–66
longArrayOf method, 128
performance of, 65–66
loops
methods 387

do-while, 19 methods. See also functions


for, 20–21 calling, 80–81
suspending functions, 161 constructors
while, 19 primary, 70, 82–83
lparams function, 330–331 secondary, 84
Lyft, 8 declaring, 80–81
doubleArrayOf, 128
M extension, 81

main function, 22–23 fromString, 309

MainActivity get, 71

Kudoo app, 220–221, 226–227 getFoods, 257

Nutrilicious app getItemCount, 216, 218–219, 246

RecyclerView, 243–244, 248–250 getters, 71, 124–125

SearchFragment, 269 intArrayOf, 128


iterator, 124
makeSection function, 309
Java
map function, 48
calling as operators, 131
Map type, 127
vararg methods, calling, 130–131
mapOf function, 46
longArrayOf, 128
mapped types, 125
observeFavorites, 287
mapping data
onAttach, 269
collections, 48
onBindViewHolder, 216, 218, 246
data types, 14
onCreate, 274
Nutrilicious app
onCreateOptionsMenu, 267
DTOs to models, 260–262
onCreateView, 269
JSON to DTOs, 258–260
onCreateViewHolder, 216, 218, 246
overview of, 257–258
onNewIntent, 267
visibilities
onViewCreated, 269
exception handling, 145–146
overloading, 141
inline functions, 145
overriding, 80–81
KClass, 143–144
recoverOrBuildSearchFragment, 274
Nothing type, 148
set, 71
overview of, 143
setItems, 232–233, 248
signature clashes, 144–145
setters, 71, 124–125
variant types, 146–148
setUpRecyclerView, 287
maps, properties delegated to, 78–79
setUpSearchRecyclerView, 270
maxBy function, 49–50
snackbar, 312
maximum, calculating, 49–50
388 methods

static, calling from Java, 140 android block, 338–339


toString, 309 dependencies, 339–340
migration experimental features, 340
to Anko layouts, 332 plugins block, 336–338
to Gradle Kotlin DSL, 335–336 Moshi, 251, 346
benefits of, 342 Gradle dependencies for, 258
buildSrc directory, 340–342 MoshiConverterFactory, 253
drawbacks of, 342–343 multiplication (*) operator, 28
Gradle settings, 336 multitasking
module build script, 338–340 cooperative, 157–158
root build script, 336–338 definition of, 152–153
to Kotlin, 7–8 preemptive, 157–158
autoconverted code, adjusting, multithreading, 152–153
355–356 mutable states, 153
full migration, 350–351
mutable variables, 13
Java-to-Kotlin converter, 355
(Mutable)List type, 127
leadership for, 346–349
MutableList type, 127
partial migration, 349–350
mutableListOf function, 46
pet projects, 353–354
(Mutable)Map type, 127
planning, 354
mutableMapOf function, 46
production code, 352–353
mutableSetOf function, 46
risks and benefits of, 345–346
mutual exclusion, 153
test code, 351–352
minBy function, 49–50
N
minimums, calculating, 49–50
named parameters, 23–24
minus sign (-)
navigation, Nutrilicious app, 242–243, 301
dec operator (--), 28
NDBNO (nutrition database number), 260
minusAssign operator (-=), 28
nested classes, 82
subtraction operator (-), 28
nesting, 323–325
MochK, 353
NetBeans, 7
Mockito, 7, 353
Netflix, 8
models
NetworkDispatcher, 256
mapping DTOs to, 260–262
RecyclerView, 215
New Project command, 210

modifiers. See keywords Newton, Joseph Fort, 315

modularizing Anko layouts, 333 nonblocking, 152

module build script, migrating to Gradle non-local returns, 44–45


Kotlin DSL not (!) operator, 15
Nutrilicious app 389

Nothing type, 148 306


@NotNull, 126 migrating to Gradle Kotlin DSL, 335–336
nullability benefits of, 342
annotations for, 128–129 buildSrc directory, 340–342
mapped types and, 125 drawbacks of, 342–343
null safety, 29 Gradle settings, 336
nullable types module build script, 338–340
casting, 90 root build script, 336–338
elvis operator (?:), 30–31 progress indicator, 312–313
overview of, 29 project setup, 242–243
safe call operator (?), 29–30 RDIs (recommended daily intake), add-
unsafe call operator (!!), 31, 355 ing, 307–311

overview of, 125 RecyclerView

platform types, 126–128 adapter, 246–248

@Nullable, 126 displaying mapped data in, 261–262

NullPointerException, 4, 8, 29 Food class, 246

Nutrient class, 308 item layout, 244–246


MainActivity layout, 243–244
NutrientListConverter, 302–303
MainActivity setup, 248–250
NutrientTypeConverter class, 303
overview of, 242–243
Nutrilicious app
Room database
data mapping
AppDatabase class, 282
DTOs to models, 260–262
coroutine dispatchers, 283
JSON to DTOs, 258–260
FavoritesDao interface, 281
overview of, 257–258
FavoritesViewModel class, 282–283,
DetailsActivity
286–287
click handler, 297
Food class, 280–281
data display, 300–301
SearchFragment, 287–288
DetailsViewModel class, 298–300
SearchListAdapter class, 283–284
FOOD_ID_EXTRA identifier, 298
toggleFavorite function, 285
item click listener, 296–297
search interface
layout, 293–296
implementation of, 265–268
navigation, 301
overview of, 265
string resources for headline, 296
SearchFragment
empty search results, indicating, 311–312
adding to UI, 273
Favorites fragment, 276–280
fragment transactions, 272
finished app, 241
getViewModel extension, 270–271
food details, storing in database, 302–
layout for, 268–269
390 Nutrilicious app

MainActivity, 269 open classes, 88–89


methods, 269–270 overriding rules for, 89
onCreate method, 274 overview of, 84–85
properties, 272 overview of, 69
recoverOrBuildSearchFragment polymorphism
method, 274 compile-time, 108
restoring, 274 parametric, 108
search intents, 275 type casting
setUpSearchRecyclerView function, ClassCastException, 90
271
nullable types, 90
storing in activity’s state, 273
smart casts, 90–91
swipe refresh, 275
type checking, 89
SwipeRefreshLayout, 271–272
type parameters, 119–121
updateListFor function, 271
variance
SearchViewModel class, 262–264
bounded type parameters, 118–119
USDA Food Reports API, fetching data
contravariance, 112
from, 288–293
covariance, 110–112
USDA Nutrition API, fetching data from
declaration-site, 113–116
API requests, performing, 256–257
Java interoperability, 115, 116
Gradle dependencies for, 250–251
star projections, 119–121
Retrofit interface, 251–256
use-site, 116–118
nutrition database number (NDBNO), 260
visibilities
class declarations, 92–93
O
interface declarations, 92–93
object expressions, 101–102
top-level declarations, 93–94
object keyword, 101–102, 130
Object[] type, 127
object orientation. See also classes; objects
objects
generics
BuildPlugins, 342
benefits of, 105
companion, 103–105
classes, 106–107
creating, 101–102
functions, 107–108
declaring, 103
reification, 108–109
DefaultDispatcher, 165
type parameters, 105–106
DTOs (data transfer objects)
inheritance
DetailsDto, 291
abstract classes, 87–88
mapping JSON data to, 258–260
closed-by-default principle, 85, 88
mapping to models, 260–262
data classes, 96
wrappers for, 290
interfaces, 85–86
pragmatic programming 391

equality methods, 141


checking, 32 override keyword, 86, 104–105
floating point, 32–33 overriding
initializing, 55 inheritance and, 89
instantiating, 69 methods, 80–81
Job, 177–181
object expressions, 101–102 P
usdaApi, 256–257 pair programming, 348
observable properties, 76–77 parallelism, 152–153
observeFavorites method, 287 parameters, function, 23–24
OkHttp, 251, 253–255 parametric polymorphism, 108
onAttach method, 269 partial migration, 349–350
onBindViewHolder method, 216, 218, 246 percent sign (%)
onCreate method, 274 rem operator (%), 28
onCreateOptionsMenu method, 267 remAssign operator (%=), 28
onCreateView method, 269 performance, DSLs (domain-specific
onCreateViewHolder method, 216, 218, 246 languages), 327–328
online resources, 7 period (.), 28
onNewIntent method, 267 Perlis, Alan J.345
onViewCreated method, 269 pet projects, integrating Kotlin into, 353–
OO. See object orientation 354
open classes, 88–89 Pinterest, 8
open keyword, 87 pipe symbol (|) operator, 15
operators plans, migration, 354
as, 90 platform types, 126–128
elvis (?:), 30–31 plugins block (Gradle Kotlin DSL), 336–338
indexed access, 47 plus function, 180
is, 89 plus sign (+), 28
Java methods as, 131 addition operator (+), 28
safe call (?), 29–30 inc operator (++), 28
table of, 28 plusAssign operator (+=), 28
ternary conditional, 18 pointers to functions, 39
unsafe call (!!), 31, 355 polymorphism, 80–81
or (||) operator, 15 compile-time, 108
Oracle, Google’s lawsuit with, 5 parametric, 108
overloading populating databases, 224–225
functions, 24–25 pragmatic programming, 4
392 preemptive multitasking

preemptive multitasking, 157–158 Q


previewing Anko layouts, 331
@Query annotation, 255
primary constructors, 70, 82–83
question mark (?)
printAll function, 119–121
elvis operator (?:), 30–31
private keyword, 92, 94
safe call operator (?), 29–30
produce coroutine builder, 191
producers, creating, 191
R
productFlavors block (Gradle Kotlin DSL),
339 race conditions, 154

production code, integrating Kotlin into, ranges in for loops, 20–21


352–353 rangeTo operator (.), 28
progress indicator (Nutrilicious app), 312–313 read-eval-print loop (REPL), 11
projects read-only variables, 13
creating receive function, 187
Kudoo app, 210–212 ReceiveChannel, 188
Nutrilicious app, 242–243 receivers, lambda expressions with, 60–61
integrating Kotlin into, 353–354 recoverOrBuildSearchFragment method,
promises, 152 274
properties RecyclerListAdapter, 215–219
adding to classes, 70–71 RecyclerView
backing fields, 72 Kudoo app
calling from Java, 133–135 adapter, 215–219
delegated layouts, 213–214
implementation of, 74–75 MainActivity, 220–221
lazy properties, 75–76 model, 215
maps, 78–79 overview of, 212–213
observable properties, 76–77 Nutrilicious app
syntax of, 74 adapter, 246–248
exposing as fields, 135–136 displaying mapped data in, 261–262
fields compared to, 71 Food class, 246
late-initialized, 72–73 item layout, 244–246
lazy, 75–76 MainActivity layout, 243–244
observable, 76–77 MainActivity setup, 248–250
SearchFragment, 272 overview of, 242–243
protected keyword, 92 Reddit Kotlin community, 7
public keyword, 92, 94 reduce function, 52
public scope, 26 reduceRight function, 52
Python, 316 referential equality operator (===), 32
SearchFragment 393

Reformat Code command (Code menu), root build scripts, migrating to Gradle Kotlin
258 DSL
Refresh All Gradle Projects command, 336 allprojects block, 337
reification, 108–109 buildscript block, 336–337
reified keyword, 109 delete task, 337–338
rem (%) operator, 28 run function, 56–58
renderNutrient function, 301, 310 runblocking coroutine builder, 163–164
rendezvous channels, 190 RxJava, 182
REPL (read-eval-print-loop), 11
requests (API), performing, 256–257 S
resolving extensions, 25–26 safe call operator (?), 29–30
REST, 151 safety, 4
restoring fragments, 274 SAM (single abstract method) conversions,
@RestrictsSuspension annotation, 185 131–132
Retrofit, 7, 251–256 Sample Data Directory, 258
Retrolambda, 348 Scala, 4
returns, non-local, 44–45 scope of extension functions, 26
Room database scoping functions
Kudoo app also, 58–59
AppDatabase class, 223–225 apply, 54–55
Gradle dependencies for, 221–222, let, 53–54
225–226 run, 56–58
LiveData class, 230–233 use, 59
MainActivity, 226–227 with, 55–56
TodoItem class, 222 Scrum, 354
TodoItemDao class, 222–223 sealed classes
ViewModel class, 227–230 calling from Java, 142–143
Nutrilicious app declaring, 98–100
AppDatabase class, 282 sealed keyword, 98–100
coroutine dispatchers, 283 Search API, 255–256, 258–260
FavoritesDao interface, 281 search interface, Nutrilicious app
FavoritesViewModel class, 282–283, implementation of, 265–268
286–287
overview of, 265
Food class, 280–281
searchable configuration, 266
SearchFragment, 287–288
SearchFragment
SearchListAdapter class, 283–284
adding to UI, 273
storing food details in, 302–306
database connection, 287–288
toggleFavorite function, 285
394 SearchFragment

fragment transactions, 272 settings (Gradle), migrating, 336


getViewModel extension, 270–271 setUpRecyclerView method, 287
layout for, 268–269 setUpSearchRecyclerView function, 271
MainActivity, 269 setUpSearchRecyclerView method, 270
methods, 269–270 sharing knowledge, 348–349
onCreate method, 274 Short data type, 13
properties, 272 short-circuiting, 15
recoverOrBuildSearchFragment method, Show Kotlin Bytecode command, 217–218
274
signatures, function, 21–22, 39, 144–145,
restoring, 274 159
search intents, 275 Simula, 69
setUpSearchRecyclerView function, 271 single abstract method (SAM) types,
storing in activity’s state, 273 131–132
swipe refresh, 275 single-expression functions, 22
SwipeRefreshLayout, 271–272 singletons, declaring, 103
updateListFor function, 271 singleTop launch mode, 266
SearchListAdapter class, 246–248, Slack, 8
283–284 Slack channel, 7
SearchViewModel class, 262–264 Smalltalk, 69
secondary constructors, 84 smart casts, 30, 90–91
send function, 187 snackbar method, 312
SendChannel, 187–188 sorted function, 50
sendEmail function, 24 sortedArray function, 50
Sequences, 6 sortedBy function, 50
sequences, lazy, 63–66 sortedDescending function, 50
creating, 63–64 sortedMapOf function, 46
drop function, 64–65
sortedSetOf function, 46
performance of, 65–66
sortedWith function, 50
take function, 64–65
sorting collections, 50–51
@SerializedName annotation, 261
SoundCloud, 354
set method, 71
Spek, 353
set operator, 28
Split Vertically command, 258
setItems method, 232–233, 248
Spring, 124
setLoading function, 313
SQL, 316
setOf function, 46
Square, 348
setters
star projections, 119–121
calling, 124–125
statements. See keywords
declaring, 71
static fields, calling from Java, 138–139
types 395

static keyword, 104 TodoItem class, 222


static methods, calling from Java, 140 TodoItemDao class, 222–223
static resolving of extensions, 25–26 toggleFavorite function, 285
Strategy pattern, 39 top-level declarations, 93–94
streams, 6, 62. See also sequences, lazy top-level extensions, calling, 137
String data type, 13, 127 toSortedMap function, 50
string interpolation, 20 toString method, 309
String! type, 127 transactions, fragment, 272
structural equality operator (==), 32 Trello, 8
subtraction (-) operator, 28 try keyword, 33–35
sumBy function, 49–50 try-catch blocks, 33–35
sums, calculating, 49–50 Twain, Mark, 3
super keyword, 87 type converters (Nutrilicious app), 302–303
support and community, 7 type inference, 14–15, 40–41
suspend keyword, 159–163, 198–200 @TypeConverter annotation, 303
suspendCoroutine function, 183 @TypeConverters annotation, 303
suspending functions, 157, 159–163, 164, types. See also nullability
198–200 algebraic, 100
suspending lambda, 164 aliases, 107
suspension points, 160 Any, 88
Swing, 182 casting
swipe refresh, 275 ClassCastException, 90
SwipeRefreshLayout, 271–272 nullable types, 90
smart casts, 30, 90–91
T checking, 89
take function, 64–65 classes versus, 110
ternary conditional operator, 18 function types, 39
test code, integrating Kotlin into, 351–352 generic type parameters, 105–106

testing frameworks, 353 mapped, 125

this keyword, 61 mapping, 14

threads, 152, 157–158. See also coroutines Nothing, 148

throw keyword, 33 nullable

throwing exceptions, 33–35 casting, 90

@Throws annotation, 145–146, 149 elvis operator (?:), 30–31

TimeoutCancellationException, 177 overview of, 29


safe call operator (?), 29–30
timeouts, handling, 177
unsafe call operator (!!), 31, 355
toast function, 330
396 types

parameters, 40–41, 105–106 complex objects, 318–320


platform types, 126–128 simple objects, 318
table of, 13–14 UserBuilder class, 321
type inference, 14–15 @UserDsl annotation, 325
variance use-site variance, 116–118, 146–148
bounded type parameters, 118–119
contravariance, 112 V
covariance, 110–112 val keyword, 12, 71, 83, 130
declaration-site, 113–116
validation, 58
star projections, 119–121
values
use-site, 116–118
floating point, 32–33
type-safe builders, 320. See also DSLs for function parameters, 23–24
(domain-specific languages)
var keyword, 12–13, 71, 83, 130
vararg methods, calling, 130–131
U
variable-argument methods, calling,
Uber, 8 130–131
Udacity, 8, 354 variables
unchecked exceptions, 35–36 declaring, 12–13
unlimited channels, 189 in DSLs (domain-specific languages), 326
unsafe call operator (!!), 31, 355 immutable, 13
updateListFor function, 271, 275, 311 mutable, 13
updateUiWith function, 313 read-only, 13
USDA Food Reports API, fetching data from, variance
288–293 bounded type parameters, 118–119
USDA Nutrition API, fetching data from contravariance, 112
API requests, performing, 256–257 covariance, 110–112
Gradle dependencies for, 250–251 declaration-site, 113–116
Retrofit interface, 251–256 Java interoperability, 115, 116
usdaApi object, 256–257 star projections, 119–121
use function, 59 use-site, 116–118, 146–148
User class variant types, 146–148
adding addresses to verticalLayout function, 330
multiple addresses, 323–325 ViewHolder class, 216–217
single addresses, 319–320 ViewModel class
declaring, 319 Kudoo app, 227–230
user function, 318 Nutrilicious app, 262–264
user groups, 348 views
user objects, building with DSLs Anko layouts, 334–335
Ziglar, Zig 397

Kudoo RecyclerView variant types, 146–148


adapter, 215–219 top-level declarations, 93–94
layouts, 213–214
MainActivity, 220–221 W
model, 215 WeightUnit enum, 309
overview of, 212–213 Wharton, Jake, 348
Nutrilicious RecyclerView when keyword, 15–19, 99–100
adapter, 246–248 where keyword, 118–119
displaying mapped data in, 261–262
while loops, 19
Food class, 246
Wigmore, Ann, 241
item layout, 244–246
wildcard generation, 146–148
MainActivity layout, 243–244
with function, 55–56
MainActivity setup, 248–250
withContext function, 176
overview of, 242–243
withTimeout function, 176
visibilities
WordPress, 8
class declarations, 92–93
wrappers
interface declarations, 92–93
for asynchronous APIs, 195–197
mapping to Java
for DTOs (data transfer objects), 290
exception handling, 145–146
inline functions, 145
X-Y-Z
KClass, 143–144
XML layouts, 335
Nothing type, 148
yield function, 184
overview of, 143
signature clashes, 144–145
Ziglar, Zig, 11
Credits
Cover: Kronstadt at Sunrise, A.F. Smith/Shutterstock.

Foreword: “The limits of … of my world,” Ludwig Wittgenstein.

Chapter 1: “The secret to getting ahead is getting started.” Mark Twain.

Chapter 2: “When you catch … when passion is born.” Zig Ziglar.

Chapter 3: “Divide each difficulty … necessary to resolve it,” René Descartes (1596–1650). Discourse on the
Method of Rightly Conducting the Reason and Seeking Truth in the Sciences.

Chapter 4: “Complexity has nothing to do with intelligence, simplicity does,” Larry Bossidy.

Chapter 4: “Generic programming centers … of useful software,” David R. Messer and Alexander A. Stepanov,
“Generic programming.” Presented at the First International Joint Conference of International Symposium
on Symbolic and Algebraic Computation (ISSAC)-88 and Applied Algebra, Algebraic Algorithms, and Error
Correcting Codes (AAECC)-6, Rome Italy, July 4-8, 1988. Lecture Notes in Computer Science, P. Gianni. 358.
Springer- Verlag, 1989, pp. 13–25. (http://stepanovpapers.com/genprog.pdf)

Chapter 5: “Synergy is better than my way or your way. It’s our way.” Stephen Covey.

Chapter 6: “Juggling is an illusion … actually task switching,” Gary Keller.

Chapter 7: “Whether you think you can, or you think you can’t—you’re right,” Henry Ford.

Figure 7.1: Screenshot of Android Studio © Google LLC.

Figure 7.2: Screenshot of Kotlin Android © Google LLC.

Figure 7.3: Screenshot of Android Studio © Google LLC.

Figure 7.4: Screenshot of Android Studio © Google LLC.

Chapter 8: “The food you … slowest form of poison,” Ann Wigmore.

Figure 8.1: Screenshot of Kotlin Android © Google LLC.

Figure 8.2: Screenshot of Kotlin Android © Google LLC.

Chapter 9: “Men build too many walls and not enough bridges,” Joseph Fort Newton.

Chapter 10: “A language that … not worth knowing,” Alan J. Perlis.


VIDEO TRAINING FOR THE IT PROFESSIONAL

LEARN QUICKLY
Learn a new technology in just hours. Video training can teach more in
less time, and material is generally easier to absorb and remember.

WATCH AND LEARN


Instructors demonstrate concepts so you see technology in action.

TEST YOURSELF

CONVENIENT

Learn more, browse our store, and watch free, sample lessons at
i n f o r m i t. co m / v i d e o

Save 50%* off the list price of video courses with discount code VIDBOB

book/eBook + video bundles, individual video lessons, Rough Cuts, Safari Books Online, non-discountable titles, titles on promotion with our retail partners, and any title featured
Photo by izusek/gettyimages

Register Your Product at informit.com/register


$FFHVVDGGLWLRQDOEHQHȴWVDQGsave 35% on your next purchase

• Automatically receive a coupon for 35% off your next purchase, valid
for 30 days. Look for your code in your InformIT cart or the Manage
Codes section of your account page.
• Download available product updates.
• Access bonus material if available.*
• Check the box to hear from us and receive exclusive offers on new
editions and related products.

*
Registration benefits vary by product. Benefits will be listed on your account page under
Registered Products.

InformIT.com—The Trusted Technology Learning Source


InformIT is the online home of information technology brands at Pearson, the world’s
foremost education company. At InformIT.com, you can:
• Shop our books, eBooks, software, and video training
• Take advantage of our special offers and promotions (informit.com/promotions)
• Sign up for special offers and content newsletter (informit.com/newsletters)
• Access thousands of free chapters and video lessons

Connect with InformIT—Visit informit.com/community

Addison-Wesley • Adobe Press • Cisco Press • Microsoft Press • Pearson IT Certification • Prentice Hall • Que • Sams • Peachpit Press

You might also like