OceanofPDF.com Kotlin for Android App Development First - Peter Sommerhoff
OceanofPDF.com Kotlin for Android App Development First - Peter Sommerhoff
1. Cover Page
2. About This E-Book
3. Title Page
4. Copyright Page
5. Contents
6. Listings
7. Foreword
8. Preface
1. Who This Book Is For
2. How to Follow This Book
3. Book Conventions
9. Acknowledgments
10. About the Author
11. I Learning Kotlin
1. 1 Introducing Kotlin
1. What Is Kotlin?
2. Goals and Language Concepts
3. Why Use Kotlin on Android?
4. Kotlin versus Java 8
5. Tool Support and Community
6. Business Perspective
7. Who’s Using Kotlin?
8. Summary
2. 2 Diving into Kotlin
1. Kotlin REPL
2. Variables and Data Types
3. Conditional Code
4. Loops and Ranges
5. Functions
6. Null Safety
7. Equality Checks
8. Exception Handling
9. Summary
3. 3 Functional Programming in Kotlin
1. Purpose of Functional Programming
2. Functions
3. Lambda Expressions
4. Higher-Order Functions
5. Working with Collections
6. Scoping Functions
7. Lazy Sequences
8. Summary
4. 4 Object Orientation in Kotlin
1. Classes and Object Instantiation
2. Properties
3. Methods
4. Primary and Secondary Constructors
5. Inheritance and Overriding Rules
6. Type Checking and Casting
7. Visibilities
8. Data Classes
9. Enumerations
10. Sealed Classes
11. Objects and Companions
12. Generics
13. Summary
5. 5 Interoperability with Java
1. Using Java Code from Kotlin
2. Using Kotlin Code from Java
3. Best Practices for Interop
4. Summary
6. 6 Concurrency in Kotlin
1. Concurrency
2. Kotlin Coroutines
3. Summary
12. II Kotlin on Android
1. 7 Android App Development with Kotlin: Kudoo App
1. Setting Up Kotlin for Android
2. App #1: Kudoo, a To-Do List App
3. Summary
2. 8 Android App Development with Kotlin: Nutrilicious
1. Setting Up the Project
2. Adding a RecyclerView to the Home Screen
3. Fetching Data from the USDA Nutrition API
4. Mapping JSON Data to Domain Classes
5. Introducing a ViewModel for Search
6. Letting Users Search Foods
7. Introducing Fragments I: The Search Fragment
8. Introducing Fragments II: The Favorites Fragment
9. Store User’s Favorite Foods in a Room Database
10. Fetching Detailed Nutrition Data from the USDA Food
Reports API
11. Integrating the Details Activity
12. Storing Food Details in the Database
13. Adding RDIs for Actionable Data
14. Improving the User Experience
15. Summary
3. 9 Kotlin DSLs
1. Introducing DSLs
2. Creating a DSL in Kotlin
3. DSL for Android Layouts with Anko
4. DSL for Gradle Build Scripts
5. Summary
4. 10 Migrating to Kotlin
1. On Software Migrations
2. Leading the Change
3. Partial or Full Migration
4. Where to Start
5. Tool Support
6. Summary
5. A Further Resources
1. Official Resources
2. Community
3. Functional Programming
4. Kotlin DSLs
5. Migrating to Kotlin
6. Testing
13. Credits
14. Glossary
15. Index
About This E-Book
EPUB is an open, industry-standard format for e-books.
However, support for EPUB and its many features varies
across reading devices and applications. Use your device or
app settings to customize the presentation to your liking.
Settings that you can customize often include font, font size,
single or double column, landscape or portrait mode, and
figures that you can click or tap to enlarge. For additional
information about the settings and features on your reading
device or app, visit the device manufacturer’s Web site.
Peter Sommerhoff
ISBN-13: 978-0-13-485419-9
ISBN-10: 0-13-485419-5
1 18
Publisher
Mark L. Taub
Acquisitions Editor
Malobika Chakraborty
Development Editor
Sheri Replin
Managing Editor
Sandra Schroeder
Full-Service Production Manager
Julie B. Nahil
Project Manager
Suganya Karuppasamy
Copy Editor
Stephanie M. Geels
Indexer
Ken Johnson
Proofreader
Larry Sulky
Technical Reviewers
Miguel Castiblanco
Amanda Hill
Ty Smith
Cover Designer
Chuti Prasertsith
Compositor
codemantra
Contents
Listings
Foreword
Preface
Acknowledgments
I: Learning Kotlin
1 Introducing Kotlin
What Is Kotlin?
Kotlin on Android
Kotlin versus Java 8
Kotlin REPL
Variables and Data Types
Variable Declarations
Basic Data Types
Type Inference
Conditional Code
Conditional Expressions
Loops and Ranges
While Loops
For Loops
Functions
Function Signatures
Shorthand for Single-Expression Functions
Main Function
Default Values and Named Parameters
Extension Functions
Infix Functions
Operator Functions
Null Safety
Nullable Types
Working with Nullables
Equality Checks
Floating Point Equality
Exception Handling
Principles of Exception Handling
Summary
Lambda Expressions
Using Lambdas in Kotlin
Higher-Order Functions
Inlining Higher-Order Functions
Working with Collections
Kotlin Collections API versus Java Collections API
Filtering Collections
Mapping Collections
Grouping Collections
Associating Collections
Folding Collections
Chaining Function Calls
Scoping Functions
Using let
Using apply
Using with
Using run
Using also
Using use
Lazy Evaluation
Using Lazy Sequences
Delegated Properties
Predefined Delegates
Delegating to a Map
Using Delegated Implementations
Methods
Extension Methods
Primary Constructors
Secondary Constructors
Abstract Classes
Open Classes
Overriding Rules
Type Checking and Casting
Type Checking
Type Casting
Smart Casts
Visibilities
Data Classes
Using Data Classes
Companion Objects
Generics
Declaration-Site Variance
Use-Site Variance
Summary
Using Operators
Using SAM Types
Accessing Properties
Exposing Properties as Fields
Getting a KClass
6 Concurrency in Kotlin
Concurrency
Challenges
State-of-the-Art Solutions
Kotlin Coroutines
Setup
Concepts
Suspending Functions
Coroutine Builders
Generators
Actors and Channels
Concurrency Styles
Coroutines in Practice
Integrating LiveData
Using Retrofit
Performing API Requests
9 Kotlin DSLs
Introducing DSLs
What Is a DSL?
Nesting Deeper
Introducing @DslMarker
Summary
10 Migrating to Kotlin
On Software Migrations
Getting Buy-In
Sharing Knowledge
Full Migration
Where to Start
Test Code
Production Code
Pet Projects
Make a Plan
Tool Support
Java-to-Kotlin Converter
A Further Resources
Official Resources
Community
Functional Programming
Kotlin DSLs
Migrating to Kotlin
Testing
Glossary
Index
Listings
2.1 Declaring Mutable Variables
2.8 If Expression
7.14
RecyclerListAdapter.onCreateViewHolder
()
7.15
RecyclerListAdapter.onBindViewHolder()
7.16 RecyclerListAdapter.getItemCount()
7.24 AppDatabase
9.40 buildSrc/build.gradle.kts
Enjoy reading.
—Bernhard Rumpe
Aachen, Germany
August 2018
Preface
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.
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.
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.
Mark
The secret to getting ahead is getting started.
Twain
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
1
Java virtual machine (JVM) and on Android. Apart from
2
this, it can be compiled down to JavaScript and can even
3
run on embedded devices and iOS 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.
1. https://developer.android.com/
2. https://developer.mozilla.org/en-US/docs/Web/javascript/
3. https://developer.apple.com/ios/
4
Kotlin was developed by JetBrains, 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 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.
4. https://www.jetbrains.com/
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
6 7 8
languages such as Scala, Ceylon, and Clojure 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.
6. https://scala-lang.org/
7. https://www.ceylon-lang.org/
8. https://clojure.org/
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 platform,
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 developers still
have to rely mostly on Java 7 at the time of writing.
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 expressions, 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
functions and lambda expressions is incorporated from the
start in Kotlin. This leads to several syntactic 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 inference, 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.
BUSINESS PERSPECTIVE
Because the intent is for Kotlin to be usable wherever Java
runs, it is important to make the transition 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 developers 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 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.
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
32
C. This book focuses on its benefits for Android app
development.
32. C Programming Language, http://www.bell-labs.com/usr/dmr/www/chist.html
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 avoidance 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.
2
Diving into Kotlin
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
1
.kts as file extension) in an IntelliJ project 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
manually to .kts if you create a regular Kotlin file with .kt
extension. Lastly, you can use the online editor on the Kotlin
2
website, 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/
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
3
repository. Additionally, the book’s companion website allows you to run all
4
listings directly inside your browser.
3. https://github.com/petersommerhoff/kotlin-for-android-app-development
4. https://www.kotlinandroidbook.com/listings
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
Click here to view code image
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 communication 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.
Byte 8 -128..127
Shor 16 -32768..32767
t
Int 32 -2147483648..2147483647
Long 64 -18446744073709551616..184467
44073709551615
Floa 32 1.4e-45..3.4028235e38
t
Doub 64 4.9e-
le 324..1.7976931348623157e308
5
All number types represent signed numbers, 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.
5. Kotlin 1.3 introduced additional unsigned integer types like UShort, UInt,
and ULong.
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
Click here to view code image
Java Note
6. http://openjdk.java.net/jeps/286
7. https://docs.oracle.com/javase/7/docs/technotes/guides/language/type-
inference-generic-instance-creation.html
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 {
when (mercury) {
when(maxSurfaceTempInK) {
else -> {
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.
when {
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 expressions is a powerful feature that can,
for instance, be used to avoid null in some cases.
} else {
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
// ...
else -> {
// More code...
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.
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
}
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 {
// Handle command...
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
Click here to view code image
println(planet)
println("$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()}".
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 function declaration is denoted as
in Listing 2.15.
Listing 2.15 Declaring a Function
Click here to view code image
} else {
Note
For this section, keep in mind that the term parameter refers to what is used at
the declarationsite 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.
With this, you can remove the return and directly assign the
expression as the value of the function, as in Listing 2.18.
Listing 2.18 Shorthand Function Notation Using Equals Sign
Click here to view code image
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.
Listing 2.19 Declaring a Main Function
Click here to view code image
println("Hello World!")
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.
exploreDirectory(directory) // Without
exploreDirectory(directory, 1) // Recursi
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
Click here to view code image
// Java code
Pizza makePizza() {
return makePizza(Arrays.asList());
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 intention 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.
n.print() // Print
Note
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 boilerplate 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
arguments. 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
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
Click here to view code image
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 operators 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.
Arithmetic
Operators
plus + 2 + 3 2.plus(3)
Assignment
Operators
plusAssign += x += x.plusAssign(
"Island" "island")
minusAssig -= x -= x.minusAssign
n 1.06 (1.06)
timesAssig *= x *= 3 x.timesAssign
n (3)
divAssign /= x /= 2 x.divAssign(2
)
remAssign %= x %= 10 x.remAssign(1
0)
Miscellaneous
Operators
Tip
There are more operators available that are not listed here. To get a complete
list of all operator 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
In 1965, Tony Hoare designed an object-oriented
8
programming language called ALGOL W. As he
admitted, a null reference was added simply because it
was easy to implement and thus tempting. 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.
8. https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-
Mistake-Tony-Hoare
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
Click here to view code image
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
Click here to view code image
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
Click here to view code image
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
Click here to view code image
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
Click here to view code image
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.
// …
try {
reducePressureBy(30)
} catch(e: IllegalArgumentException) {
} catch(e: IllegalStateException) {
} finally {
} catch(e: NumberFormatException) {
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
Click here to view code image
throw IllegalArgumentException(message)
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 exception
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.
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
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.
Java Note
1
Kotlin’s lazy sequences are conceptually the same as Java 8 Streams, and
their usage is very similar as well.
1. https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-
summary.html
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.
Java Note
There are no proper function types in Java 8. Instead, there is only a fixed
3
number of predefined function types.
3. https://docs.oracle.com/javase/8/docs/api/java/util/function/package-
summary.html
LAMBDA EXPRESSIONS
Lambda expressions allow the definition of unnamed
functions at the call site, without the boilerplate 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.
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.
Listing 3.5 Type Inference for Lambda Arguments
Click here to view code image
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
Click here to view code image
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
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.
HIGHER-ORDER FUNCTIONS
Higher-order functions take another function as input or
return a function. Consider the definition of a higher-order
function called twice shown in Listing 3.9.
Listing 3.9 Defining a Higher-Order Function
Click here to view code image
fun twice(f: (Int) -> Int): (Int) -> Int = { x -> f(f
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.
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
Click here to view code image
range.forEach {
return false
return false
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
4
well.
4. https://github.com/Kotlin/kotlinx.collections.immutable
// Collections
// Arrays
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
linkedSe linkedMa
tOf pOf
sortedSe sortedMa
tOf pOf
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 corresponding 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.
votes[1] // false
testData[2] // 5
countryToCapital["Germany"] // "Berlin"
This internally calls the set method defined for each of these
mutable collection types, for instance, votes.set(1,
true).
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
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
Click here to view code image
val sentence = "This is an example sentence with seve
.groupBy { it.length }
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
Click here to view code image
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, respectively. 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
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
Click here to view code image
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 parameter. They are shown
in Listing 3.29.
Listing 3.29 Sorting Collections
Click here to view code image
The default sorting operations in the first two lines use natural
ordering, in this case the lexicographical 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.
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.
Figure 3.1 Visualization of the fold function
.take(10) //
.map { it.username } /
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
it.close()
result
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.
weather?.let {
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
countryToCapital.apply {
putIfAbsent("France", "Paris")
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
Click here to view code image
Using with
The with function behaves almost like apply and has
two major use cases.
To encapsulate multiple calls on the same object
putIfAbsent("England", "London")
putIfAbsent("Spain", "Madrid")
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.
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.
= mutableMapOf("Germany" to "Berlin")
putIfAbsent("Germany", "Berlin")
keys
run {
println("Running lambda")
val a = 11 * 13
validate(username, password)
println(displayName)
Using also
This last higher-order function from Standard.kt has
two main use cases.
Performing ancillary operations like validation or logging
requireNotNull(it)
require(it!!.monthlyFee > 0)
.map { it.monthlyFee }
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
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 familiarize 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
Click here to view code image
bind("johndoe")
bind(42)
bind(true)
}.also {
}.run {
}.filter {
it.value.isNotEmpty()
}.also {
}.map {
it.key
Whether the scoping function returns the object on which it was called or
the lambda’s return value
LAZY SEQUENCES
Having covered higher-order functions and lambda
expressions, the last cornerstone of functional
programming that is left to discuss is lazy evaluation.
More specifically, this section covers sequences in Kotlin
—a data structure using lazy evaluation.
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 computations
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 differently 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
Click here to view code image
animals.filter { it.startsWith("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
evaluation 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.
println(sequence.joinToString()) // -5, 0, 5
.filter { it.startsWith("W") }
.joinToString()
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
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
// Not good
cities.filter { it.startsWith("W") }
.joinToString()
// Better
cities.filter { it.startsWith("W") }
.joinToString()
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.
filter: Washington
filter: Houston
filter: Seattle
filter: Worcester
map: Washington
map: Worcester
cities.asSequence()
.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
Click here to view code image
filter: Washington
filter: Houston
filter: Seattle
filter: Worcester
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
In this chapter, you explored how functional programming
emphasizes immutability and extensive 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
modularization and writing concise code.
class Task {
// Implement class here...
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.
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
Click here to view code image
class Task {
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.
class CarTest {
@BeforeEach
fun setup() {
// ...
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 reusable
because the delegate encapsulates it.
class Cat {
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
return name
}
if (value.isNotBlank()) {
this.name = value
Lazy Properties
import java.time.LocalDate
import java.time.temporal.ChronoUnit
println("Computing age...")
ChronoUnit.YEARS.between(birthday, LocalDate.now(
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:
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 star tup and thus prevent the app from
not responding or even crashing.
Observable Properties
import java.time.LocalDate
import kotlin.properties.Delegates
// …
when (newValue) {
}
enum class Mood { GRUMPY, HUNGRY, SLEEPY } // Enums
Vetoable Properties
jsonData["mood"] = Mood.valueOf(jsonData["mood"] as S
println(john.age) // Prints 42
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"].
interface Kickable {
fun kick()
}
// Existing implementation of Kickable
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.
class Foo {
fun plainMethod() { … }
obj.plainMethod()
class Container {
println(toString()) // Calls In
println([email protected]()) // Calls C
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 problematic 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.
class Company {
class Nested {
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 possible modifiers) as demonstrated in
Listing 4.19. Parameters are defined the same way as in
normal methods.
init {
priority = Math.max(_priority, 0) // Uses
init {
require(priority >= 0) /
}
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.
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
Click here to view code image
Tip
This is obsolete thanks to default parameter values that allow you to concisely
implement optional parameters:
INHERITANCE AND
OVERRIDING RULES
Inheritance is one of the cornerstones of OO. At its core,
OO is about abstraction and programming the differences
between classes. This is enabled by inheriting shared logic
from a parent 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.
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
Click here to view code image
interface Searchable {
Java Note
2
Java 8 introduced interfaces with default method implementations as well.
2. https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html
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
implementations 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
Click here to view code image
}
class Task(val title: String, priority: Int) : Issue(
// …
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 override any open members. Closed members
cannot be overridden.
Note that you can also override properties in primary
constructors using class Task(…, override 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
3
else prohibit it” (see Effective Java by Joshua Bloch ).
When classes are open by default, they often allow
inheritance without being designed for it, which can lead
to fragile code.
3. https://www.pearson.com/us/higher-education/program/Bloch-Effective-Java-
3rd-Edition/PGM1763855.html
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.
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.
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
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 Component defines the function
component(), Composite has an additional function
composite(), and Leaf has a function leaf().
Listing 4.31 Smart Casts
Click here to view code image
when (comp) {
}
if (comp is Composite && comp.composite() == 16) {}
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 facilitators of information hiding. They allow you to
define what’s accessible from where.
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.
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.
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.
val a = "public"
class Unrelated {
val p = Parent()
Note
One more thing to consider with visibilities is that inline methods cannot access
class properties 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?
// toString
// equals
// componentN
// copy
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.
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 legitimate 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:
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, directions, or a set of colors.
Listing 4.36 Declaring a Simple Enum Class
Click here to view code image
OPEN, PAID
OPEN(true) {
},
PAID(false) {
println(status.name) // PAID
println(status.ordinal) // 1
println(status.billable) // false
println(PaymentStatus.valueOf("OPEN")) // OPEN
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
Click here to view code image
Note
println(evaluate(formula)) // 42.0
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:
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.
val x = vertex
val y = covertex
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.
scrollView.setOnDragListener(object : View.OnDragList
Log.d(TAG, “Dragging...”)
return true
})
Tip
Java interfaces with just a single abstract method (called SAM interfaces) such
as OnDragListener are more easily constructed using a lambda expression:
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.
class ReturnsObjectExpressions {
fun access() {
}
}
import javafx.scene.Scene
fun buildHomeScene() { … }
fun buildSettingsScene() { … }
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 singletons 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.
Listing 4.47 A Factory as Companion Object
Click here to view code image
}
val car = Car.defaultCar() // Calls companion method
interface CarFactory {
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 @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:
Click here to view code image
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
Click here to view code image
println(box.element) // 17
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
) : BinaryTree<T>()
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:
Click here to view code image
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.
Listing 4.51 Declaring and Using a Generic Function
Click here to view code image
// Without parameters
Note
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.
Listing 4.52 A Generic Function that Accesses Its Type Parameter
Click here to view code image
println(elements.filterByType(Int::class.javaObjectTy
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.
println(elements.filterByType<Int>()) //
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.
Task? No Yes
List<Task> 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 variance, 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.
Listing 4.55 Covariance of Subclasses in Assignments
Click here to view code image
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:
arr[0] = 3.1415 // Wo
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.
Listing 4.58 Invariance of Java Collections (Type-Safe)
Click here to view code image
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.
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
Click here to view code image
vehicle.damaged = false
Java Note
6. http://openjdk.java.net/jeps/300
Keyw out in -
ord
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 familiar 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.
consumer.add(4)
// Same as before
// …
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
Click here to view code image
fun repair(t: T)
interface Repairable { … }
T : Repairable {
fun repair(t: T)
}
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:
Click here to view code image
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.
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
Click here to view code image
array.forEach(::println)
}
printAll(arrayOf(1, 2, 3))
// Star projections
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.
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
Click here to view code image
import com.google.common.math.Stats
import java.util.ArrayList
// Using the standard library
println(stats.sum()) // 108.0
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
1 2
any third-party libraries and frameworks, whether it be Spring, JUnit, or the
Android Software Development Kit (SDK).
1. https://spring.io/
2. https://junit.org/
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
Click here to view code image
// Java
// Kotlin
println(gs.readWrite)
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 information expressed in the language itself
because every variable may potentially be null (except
primitive 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.
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
Note
You cannot explicitly create platform types yourself. Types such as String!
are not valid syntax in Kotlin and are only used by the compiler and IDE in
order to communicate types to you.
// Java
// Kotlin
println(str.length) // 15
println(nullable?.length) // 15
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.
6. https://projectlombok.org/features/NonNull
7
Eclipse’s nullability annotations from
org.eclipse.jdt.annotation.
7. http://www.eclipse.org/
// Java
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.
// Java
class KeywordsAsIdentifiers {
// Kotlin
kai.`val`
kai.`object`
kai.`in`(listOf(1, 2, 3))
kai.`fun`()
// Java
return Arrays.asList(strings);
// Kotlin
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
Click here to view code image
// Java
// Kotlin
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.
// Java
// Kotlin
// Kotlin
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.
// Kotlin
class KotlinClass {
// 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 property
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 {
// Java
return this.fixed;
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).
@JvmField
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.
// sampleName.kt
package com.example
// sampleName.kt
// ...
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
Click here to view code image
// Kotlin
@file:JvmName("Notifications")
// Kotlin
class Notifier {
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 generated from:
Properties in object declarations
Constant properties
// Kotlin
class Car {
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
Click here to view code image
// Kotlin
object Cache {
class Car {
Car.produceCar(); // Static
// Kotlin
// Java
ArrayUtils.join(languages); // Skip
// Kotlin
// Java
Component comp = new Composite(asList(new Leaf(1), ne
out.println("It's a Leaf");
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
Click here to view code image
// Kotlin
// Java
p2.equals(p3); // true
Visibilities
The available visibilities don’t map exactly between
Kotlin and Java, plus you have top-level declarations in
Kotlin. So let’s explore how visibilities are mapped to
Java. First, some visibilities can be mapped trivially.
Private members remain private.
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.
Listing 5.22 Getting KClass and Class References
// Java
import kotlin.jvm.JvmClassMappingKt;
import kotlin.reflect.KClass;
// Kotlin
import kotlin.reflect.KClass
fun List<Customer>.validate() { … }
fun List<CreditCard>.validate() { … }
customers.validate()
ccs.validate()
// Kotlin
if (!predicate) println(message())
Intrinsics.checkParameterIsNotNull(args, "args");
if (!predicate$iv) {
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 (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
Click here to view code image
// Kotlin
import java.io.*
@Throws(FileNotFoundException::class) // Generates t
// Java
import java.io.FileNotFoundException;
// …
} catch (FileNotFoundException e) {
}
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
Click here to view code image
// Without @Throws
// With @Throws
// Kotlin
// Kotlin
// Java
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.
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 implementation 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.
throw AssertionError(message)
// Java
Use the accessor prefixes get, set, and is so that they are
automatically accessible as properties from Kotlin.
Do not define methods that can be used as operators from Kotlin unless
they indeed make sense for the corresponding operator.
12
Do not use identifiers in Java that are hard keywords in Kotlin to avoid
escaping.
12. https://kotlinlang.org/docs/reference/keyword-reference.html#hard-
keywords
CONCURRENCY
1
In today’s world of REST 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.
1. Acronym stands for “representational state transfer.”
State-of-the-Art Solutions
In a world of multicore processors, multitasking is the
primary means to improve the performance 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
communication 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.
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
updateUi(weatherData)
fetchUser(userId)
KOTLIN COROUTINES
Having discussed the virtues, challenges, and approaches
of concurrency, this section introduces 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.
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:
Click here to view code image
implementation "org.jetbrains.kotlinx:kotlinx-corouti
implementation "org.jetbrains.kotlinx:kotlinx-corouti
2
You can replace the version number with the latest one. 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
3
detailed setup guide on GitHub.
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
Concepts
With coroutines, the Kotlin team intended to generalize
the concepts of async-await and generators (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 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.
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
suspending 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
Click here to view code image
// Callback-based
// Future-based
updateUi(weatherData)
}
// Call-site
updateWeather()
updateUi(weatherData)
} catch(e: Exception) {
// Handle exception
} finally {
// Cleanup
updateUi(weatherData, it)
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.
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.
import kotlinx.coroutines.runBlocking
runBlocking {
updateWeather()
updateWeather()
// In a test case…
Note
import kotlinx.coroutines.*
launch {
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.
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 create 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.
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
Click here to view code image
import kotlinx.coroutines.*
println("Coroutine started")
delay(1000)
println("Coroutine finished")
println("Script continues")
println("Script finished")
import kotlinx.coroutines.*
runBlocking {
launch {
delay(1000)
print("+")
jobs.forEach { it.join() }
import kotlinx.coroutines.*
runBlocking {
repeat(10) {
delay(300) // Cooperative d
delay(1000)
import kotlinx.coroutines.*
runBlocking {
repeat(10) {
Thread.sleep(300) // Non-cooperati
delay(1000)
runBlocking {
repeat(10) {
if (isActive) { // Cooperative
Thread.sleep(300)
println("${it + 1} of 10...")
delay(1000)
job.cancelAndJoin()
import kotlinx.coroutines.*
delay(1000)
delay(1000)
}
runBlocking {
println("Result: $result") // 42
runBlocking {
import kotlinx.coroutines.*
delay(1000); return 7
runBlocking {
import kotlinx.coroutines.*
runBlocking {
try {
}
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 operation 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.getCompletionExceptionOrNull to get
access to the thrown exception. In most cases, the approach in
Listing 6.25 should suffice and is more readable.
Note
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
import kotlinx.coroutines.android.UI
import kotlinx.coroutines.launch
updateUi(weatherData)
val UI = HandlerContext(Handler(Looper.getMainLooper(
launch { … }
launch(DefaultDispatcher) { … }
launch(CommonPool) { … }
launch(newSingleThreadContext("MyNewThread")) { … }
launch(coroutineContext) { … }
import kotlinx.coroutines.*
import kotlin.coroutines.experimental.coroutineContex
runBlocking {
jobs += launch {
delay(500)
jobs += launch(Unconfined) {
delay(500)
jobs += launch(coroutineContext) {
delay(500)
jobs.forEach { it.join() }
.commonPo
ol-worker-1
import kotlinx.coroutines.*
import kotlinx.coroutines.android.UI
withContext(UI) {
updateUi(weatherData)
// Call-site
launch { updateWeather() }
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:
Click here to view code image
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.
import kotlinx.coroutines.*
runBlocking {
withTimeout(1200) {
repeat(10) {
delay(500)
println("${it + 1} of 10")
import kotlinx.coroutines.*
runBlocking {
try {
withTimeout(1200) {
repeat(10) {
delay(500)
println("${it + 1} of 10")
println("Time is up!")
} finally {
A Coroutine’s Job
import kotlinx.coroutines.*
runBlocking {
repeat(10) { i ->
delay(300 * (i + 1))
}
}
delay(1000)
import kotlinx.coroutines.*
import kotlinx.coroutines.Job
import android.support.v7.app.AppCompatActivity
super.onDestroy()
job.cancel()
import android.view.View
import kotlinx.coroutines.NonCancellable
val View.contextJob
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
Click here to view code image
import kotlinx.coroutines.*
// CoroutineName
launch(name) { … }
// CoroutineExceptionHandler
exception.printStackTrace()
launch(exceptionHandler) { … }
// CoroutineName + CoroutineExceptionHandler
launch(name + exceptionHandler) { … }
// Job + CoroutineDispatcher
import kotlinx.coroutines.*
import kotlin.coroutines.experimental.ContinuationInt
launch(name + exceptionHandler) {
println("Context: ${coroutineContext}")
println("Job: ${coroutineContext[Job]
println("Dispatcher: ${coroutineContext[Cont
println("Name: ${coroutineContext[Coro
runBlocking {
println("Moving on...")
delay(1000)
println("Joined coroutine")
import kotlinx.coroutines.future.*
runBlocking {
fetchValueAsync().thenApply { it * 6 }
.await()
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 coroutines 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)
Click here to view code image
import kotlin.coroutines.experimental.buildSequence
yield(1)
var a = 0
var b = 1
while (true) {
val next = a + b
yield(next)
a = b
b = next
fibonacci.take(10).forEach { println(it) } // 1, 1,
Note
@RestrictsSuspension
Note
7
In the original actor model described by Carl Hewitt, there are no channels.
Actors communicate 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.
7. Hewitt talks about his concept here: https://youtu.be/7erJ1DV_Tlo
import kotlinx.coroutines.channels.actor
import kotlinx.coroutines.runBlocking
println(message)
}
runBlocking {
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.
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.
import kotlinx.coroutines.channels.actor
import kotlinx.coroutines.runBlocking
runBlocking {
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.
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.
import kotlinx.coroutines.channels.*
import kotlinx.coroutines.*
runBlocking {
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.
Tip
I recommend reading the source code documentation in the Kotlin standard
library. It provides 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.
import kotlinx.coroutines.channels.*
import kotlinx.coroutines.*
import kotlinx.coroutines.selects.select
runBlocking {
while (true) {
channel1.close(); channel2.close()
}
Note
The select function is biased to the first clause, meaning that if multiple
clauses are selectable 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 randomly shuffles the clauses each time before selecting, making
the selection fair.
import kotlinx.coroutines.channels.*
import kotlinx.coroutines.*
runBlocking {
repeat(3) { n ->
launch {
while (true) {
channel.take(10).consumeEach { println(it) }
channel.close()
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
runBlocking {
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.
when (tx) {
bankAccount.send(Withdrawal(1700))
bankAccount.send(Deposit(4400))
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
12
control and resume later. Similarly, as mentioned, the actor model was
13
introduced by Carl Hewitt back in 1973.
11. In fact, you can easily simulate them using fun go(block: suspend
() -> Unit) = CommonPool.runParallel(block)
12. http://www.melconway.com/Home/pdf/compiler.pdf
13. https://www.researchgate.net/scientific-contributions/7400545_Carl_Hewitt
Remember that Kotlin does not claim to be innovative but rather an
amalgamation of wellknown 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
Click here to view code image
// Callbacks
// Futures
// Coroutines
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 interoperability 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 concurrency 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.
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.
Listing 6.58 Enabling Coroutine Debugging on Android
Click here to view code image
super.onCreate()
System.setProperty("kotlinx.coroutines.debug",
// In AndroidManifest.xml
<application
android:name=".App" …>
</application>
job.cancelFutureOnCompletion(future)
return future
// …
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 continuations 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.
import kotlinx.coroutines.future.future
import kotlinx.coroutines.*
import java.util.concurrent.CompletableFuture
suspend fun <T> CompletableFuture<T>.myAwait(): T {
else continuation.resumeWithException(exception
runBlocking {
println(completable.myAwait()) // 42
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 references 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 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
Click here to view code image
import kotlinx.coroutines.future.future
// Java
val a = 17
println(a)
return 0
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.
is 0 -> {
is 1 -> {
val a = 17
is 2 -> {
println(a)
return 0
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.
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.
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.
Continuations are essentially callbacks but can also store the state of a
coroutine at a suspension point.
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.
buildscript {
// …
dependencies {
// …
classpath "org.jetbrains.kotlin:kotlin-gradle-plu
// …
...
dependencies {
...
implementation "org.jetbrains.kotlin:kotlin-stdli
}
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
1
kotlin,” and select the desired action.
1. https://developer.android.com/studio/intro/keyboard-shortcuts
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).
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 corresponding 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
Click here to view code image
implementation "org.jetbrains.kotlin:kotlin-stdlib-jd
implementation "org.jetbrains.kotlin:kotlin-stdlib-jd
implementation "org.jetbrains.kotlin:kotlin-reflect:$
testImplementation "org.jetbrains.kotlin:kotlin-test:
testImplementation "org.jetbrains.kotlin:kotlin-test-
// …
dependencies {
implementation 'com.google.dagger:dagger:2.17'
kapt 'com.google.dagger:dagger-compiler:2.17' //
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
2
instructions from the Android Developers website to create a project with a
Basic Activity and Kotlin support.
2. https://developer.android.com/studio/projects/create-project
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
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
3
Virtual Device (AVD) if you don’t have one yet.
3. Set up a virtual device: https://developer.android.com/studio/run/managing-avds
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
onOptionsItemSelected 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 content for the .gitignore file. I recommend using one
4
configured for Android, Android Studio, Kotlin, and Java.
4. https://www.gitignore.io/api/java%2Ckotlin%2Candroid%2Candroidstudio
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 contents 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.
5
The code for the Kudoo app is on GitHub 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.
5. https://github.com/petersommerhoff/kudoo-app
<android.support.constraint.ConstraintLayout
app:layout_behavior="@string/appbar_scrolling_vie
<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>
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.
Listing 7.8 RecyclerView Item Layout
<LinearLayout xmlns:android="http://schemas.android.c
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_med
<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>
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
manually 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.
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
import android.support.v7.widget.RecyclerView
import com.example.kudoo.model.TodoItem
class RecyclerListAdapter(
) : RecyclerView.Adapter<RecyclerListAdapter.ViewHold
// …
}
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.
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(…) : … {
// …
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.
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 explicitly 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
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.todo_item.* //
class RecyclerListAdapter(…) : … {
// …
class ViewHolder(
) : RecyclerView.ViewHolder(containerView), LayoutC
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 Decompile.
Alternately, you can use the Tools menu; under Kotlin there’s
the option Show Kotlin Bytecode.
import android.view.LayoutInflater
import android.view.ViewGroup
class RecyclerListAdapter(…) : … {
// …
return ViewHolder(itemView)
This first creates a new view (a list item) by inflating the list
item layout. It does so without attaching 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()
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(
) : RecyclerView.Adapter<RecyclerListAdapter.ViewHold
return ViewHolder(itemView)
holder.bindItem(items[position])
class ViewHolder(
) : RecyclerView.ViewHolder(containerView), LayoutC
tvTodoTitle.text = todoItem.title
cbTodoDone.isChecked = false
}
}
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 findViewById. Listing 7.18 shows the
setup logic.
Listing 7.18 Setting Up the RecyclerView with the Adapter
import android.support.v7.widget.*
import com.example.kudoo.model.TodoItem
import com.example.kudoo.view.main.RecyclerListAdapte
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.content_main.*
// …
adapter = RecyclerListAdapter(sampleData()) // P
layoutManager = LinearLayoutManager(this@MainActi
itemAnimator = DefaultItemAnimator() // O
addItemDecoration(
DividerItemDecoration(this@MainActivity, Divi
}
private fun sampleData() = mutableListOf(
TodoItem("Implement RecyclerView"),
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.*.
// …
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.
dependencies {
// …
implementation "android.arch.persistence.room:run
kapt "android.arch.persistence.room:compiler:$roo
import android.arch.persistence.room.Entity
import android.arch.persistence.room.PrimaryKey
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.OnConflictStrate
import com.example.kudoo.model.TodoItem
@Dao
interface TodoItemDao {
@Delete
import android.arch.persistence.room.*
import com.example.kudoo.model.TodoItem
companion object {
if (INSTANCE == null) {
.build()
}
return INSTANCE!!
import android.arch.persistence.db.SupportSQLiteDatab
import kotlinx.coroutines.experimental.*
val DB = newSingleThreadContext("DB") // CoroutineCo
companion object {
if (INSTANCE == null) {
.addCallback(prepopulateCallback(ctx)) /
.build()
return INSTANCE!!
super.onCreate(db)
populateWithSampleData(ctx)
}
private fun populateWithSampleData(ctx: Context)
with(getDatabase(ctx).todoItemDao()) { // Us
insertTodo(TodoItem("Create entity"))
implementation "org.jetbrains.kotlinx:kotlinx-corouti
implementation "org.jetbrains.kotlinx:kotlinx-corouti
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.*
import com.example.kudoo.db.*
// …
db = AppDatabase.getDatabase(applicationContext)
// …
launch {
}
layoutManager = LinearLayoutManager(this@MainActiv
itemAnimator = DefaultItemAnimator()
addItemDecoration(
DividerItemDecoration(this@MainActivity, Divid
withContext(DB) { db.todoItemDao().loadAllTodos
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 activity. 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.
dependencies {
// …
implementation "android.arch.lifecycle:extensions:$
kapt "android.arch.lifecycle:compiler:$lifecycle_ve
import android.app.Application
import android.arch.lifecycle.AndroidViewModel
import com.example.kudoo.db.*
import com.example.kudoo.model.TodoItem
import kotlinx.coroutines.experimental.*
// …
viewModel = getViewModel(TodoViewModel::class) /
setUpRecyclerView()
import android.arch.lifecycle.*
import android.support.v4.app.FragmentActivity
import kotlin.reflect.KClass
ViewModelProviders.of(this).get(modelClass.java)
Tip
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.
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).
import android.arch.lifecycle.LiveData
@Dao
interface TodoItemDao {
// …
import android.arch.lifecycle.LiveData
dao.loadAllTodos()
// …
import android.arch.lifecycle.LiveData
import kotlinx.coroutines.experimental.android.UI
// …
with(recyclerViewTodos) {
adapter = RecyclerListAdapter(mutableListOf())
// …
todosLiveData.observe(this@MainActivity, Observ
todos?.let {
val adapter = (recyclerViewTodos.adapter as
})
class RecyclerListAdapter(
) : RecyclerView.Adapter<RecyclerListAdapter.ViewHold
// …
this.items.clear()
this.items.addAll(items)
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
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/TextAp
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/TextAp
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
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.
Listing 7.38 Layout for the FloatingActionButton
Click here to view code image
<android.support.design.widget.CoordinatorLayout …>
<!-- … -->
<android.support.design.widget.FloatingActionButt
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
import android.content.Intent
import com.example.kudoo.view.add.AddTodoActivity
// …
fab.setOnClickListener {
// …
// …
setUpRecyclerView()
setUpFloatingActionButton()
}
}
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_to
import kotlinx.coroutines.experimental.launch
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_todo)
viewModel = getViewModel(TodoViewModel::class)
setUpListeners()
btnAddTodo.setOnClickListener {
launch(DB) { viewModel.add(TodoItem(newTodo)) }
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 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.
<activity android:name=".view.add.AddTodoActivity"
android:parentActivityName=".MainActivity">
<meta-data android:name="android.support.PARENT_AC
android:value="com.example.kudoo.MainActivity"
</activity>
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.
class RecyclerListAdapter(
) : RecyclerView.Adapter<RecyclerListAdapter.ViewHold
// …
// …
cbTodoDone.setOnCheckedChangeListener { _, _ ->
onItemCheckboxClicked(todoItem)
import kotlinx.coroutines.experimental.android.UI
with(recyclerViewTodos) {
adapter = RecyclerListAdapter(mutableListOf(),
// …
// …
launch(DB) { viewModel.delete(todo) }
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.
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.
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
1. https://github.com/petersommerhoff/nutrilicious-app
<menu xmlns:android="http://schemas.android.com/apk/r
<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_24
android:title="@string/title_my_foods" />
</menu>
<string name="title_home">Home</string>
when(it.itemId) {
return@OnNavigationItemSelectedListener true
return@OnNavigationItemSelectedListener true
false
// …
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.
<android.support.constraint.ConstraintLayout
app:layout_behavior="@string/appbar_scrolling_vie
<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/navi
<android.support.design.widget.BottomNavigationVi
android:id="@+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="paren
android:background="?android:attr/windowBackg
app:menu="@menu/navigation" />
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/and
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-aut
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/ivSt
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Gingerbread" />
<TextView
android:id="@+id/tvFoodType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/tvF
app:layout_constraintStart_toStartOf="@id/tvF
app:layout_constraintRight_toLeftOf="@id/ivSt
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_d
app:layout_constraintBottom_toBottomOf="paren
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
// In res/values/strings.xml
<string name="content_description_star">favorite</str
// 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!
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
Click here to view code image
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.* // I
class SearchListAdapter(
) : RecyclerView.Adapter<ViewHolder>() {
) : RecyclerView.ViewHolder(containerView), LayoutC
tvFoodName.text = food.name
tvFoodType.text = food.type
android.R.drawable.btn_star_big_on
} else {
android.R.drawable.btn_star_big_off
}
ivStar.setImageResource(image)
import android.support.v7.widget.*
// …
adapter = SearchListAdapter(sampleData())
layoutManager = LinearLayoutManager(this@MainActi
addItemDecoration(DividerItemDecoration(
this@MainActivity, LinearLayoutManager.VERTIC
))
setHasFixedSize(true)
import com.example.nutrilicious.model.Food
// …
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setUpSearchRecyclerView()
navigation.setOnNavigationItemSelectedListener(navL
Endpoint: https://api.nal.usda.gov/ndb/search/
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.
3
For this app, you will use OkHttp to access the network,
4 5
Retrofit to access the API endpoints, and Moshi to map the
JSON data to Kotlin objects. As always, the first step is to add
the corresponding dependencies to your module’s
build.gradle file as in Listing 8.13.
3. https://github.com/square/okhttp
4. https://github.com/square/retrofit
5. https://github.com/square/moshi
dependencies {
// …
def retrofit_version = "2.4.0"
implementation "com.squareup.retrofit2:retrofit:$
implementation "com.squareup.retrofit2:converter-
implementation "com.squareup.okhttp3:logging-inte
implementation "com.squareup.okhttp3:okhttp:$okht
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
Click here to view code image
import com.example.nutrilicious.BuildConfig
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 directory. 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
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 properties are grouped by project.
The next step is to add this property to the project’s build
configuration. 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
Click here to view code image
buildTypes {
debug {
release { … }
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactor
// …
.baseUrl(BASE_URL)
.client(buildHttpClient())
.addConverterFactory(MoshiConverterFactory.create
.build()
import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
// …
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor()) // Logs AP
.addInterceptor(apiKeyInterceptor()) // Adds AP
.build()
import okhttp3.logging.HttpLoggingInterceptor
// …
level = if (BuildConfig.DEBUG) {
} else {
HttpLoggingInterceptor.Level.NONE // Otherwise n
}
}
import okhttp3.Interceptor
// …
.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.intercept 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
Click here to view code image
"api_key" to API_KEY
import okhttp3.ResponseBody
import retrofit2.Call
import retrofit2.http.*
interface UsdaApi {
@GET("search?format=json") // Is appen
fun getFoods(
): Call<ResponseBody> // Allows t
https://api.nal.usda.gov/ndb/search?format=json&q=raw
&ds=Standard%20Reference&offset=0.
<manifest …>
<uses-permission android:name="android.permission
<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
Click here to view code image
implementation "org.jetbrains.kotlinx:kotlinx-corouti
implementation "org.jetbrains.kotlinx:kotlinx-corouti
import kotlinx.coroutines.newFixedThreadPoolContext
import com.example.nutrilicious.data.network.*
import com.example.nutrilicious.model.Food
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.launch
// …
// …
launch(NETWORK) {
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).
implementation "com.squareup.moshi:moshi:$moshi_versi
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_
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.
7
You can simply copy and paste the data from your browser and then use
Code, Reformat Code from the menu to fix the code formatting in Android
Studio if necessary.
7. https://api.nal.usda.gov/ndb/search/?
format=json&ds=Standard%20Reference&q=raw&sort=r&max=10&api_key=
DEMO_KEY
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.
Listing 8.30 Search API JSON Format
Click here to view code image
"list": {
"q": "raw",
"sort": "r",
"item": [
"offset": 0,
"ndbno": "11165",
"ds": "SR",
"manu": "none"
},
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
Click here to view code image
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
class ListWrapper<T> {
@JsonClass(generateAdapter = true)
class ItemWrapper<T> {
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
Click here to view code image
@JsonClass(generateAdapter = true)
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
Click here to view code image
import com.example.nutrilicious.data.network.dto.*
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 accordingly. 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
Click here to view code image
import kotlinx.coroutines.android.UI
import kotlinx.coroutines.withContext
// …
// …
launch(NETWORK) {
(rvFoods.adapter as SearchListAdapter).setIte
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.
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
functionality 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, potentially 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 architecture. 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
Click here to view code image
implementation "android.arch.persistence.room:runtime
kapt "android.arch.persistence.room:compiler:$room_ve
implementation "android.arch.lifecycle:extensions:$li
import com.example.nutrilicious.data.network.dto.*
import retrofit2.Call
req.execute().body()?.list?.item ?: emptyList()
import com.example.nutrilicious.data.network.*
import com.example.nutrilicious.model.Food
import kotlinx.coroutines.withContext
return foodDtos.map(::Food)
// …
}
import com.example.nutrilicious.view.common.getViewMo
// …
// …
navigation.setOnNavigationItemSelectedListener(na
searchViewModel = getViewModel(SearchViewModel::c
// …
// …
import android.arch.lifecycle.*
import android.support.v4.app.FragmentActivity
import kotlin.reflect.KClass
return ViewModelProviders.of(this).get(modelClass.j
// …
searchViewModel = getViewModel(SearchViewModel::cla
(rvFoods.adapter as SearchListAdapter).setItems
}
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.
launch(NETWORK) {
withContext(UI) {
(rvFoods.adapter as SearchListAdapter).setItems
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
coroutine. Next, remove the test request in onCreate.
8
Instead, you’ll implement a proper Android Search Interface
that lets users search the foods in which they are interested.
8. https://developer.android.com/training/search/setup
<menu xmlns:app="http://schemas.android.com/apk/res-a
xmlns:android="http://schemas.android.com/apk/res
<item android:id="@+id/search"
android:title="@string/search_title"
android:icon="@android:drawable/ic_menu_searc
app:showAsAction="always"
app:actionViewClass="android.widget.SearchVie
</menu>
<searchable xmlns:android="http://schemas.android.com
android:label="@string/app_name"
android:hint="@string/search_title" />
<activity
android:launchMode="singleTop" //
android:name=".view.main.MainActivity"
android:label="@string/app_name">
<meta-data android:name="android.app.searchable" /
android:resource="@xml/searchable" />
<intent-filter>
<action android:name="android.intent.action.SEARC
</intent-filter>
…
</activity>
import android.app.SearchManager
import android.widget.SearchView
import android.content.Context
import android.view.Menu
// …
// …
menuInflater.inflate(R.menu.search_menu, menu)
(menu.findItem(R.id.search).actionView as SearchV
setSearchableInfo(searchManager.getSearchableIn
}
return true
import android.content.Intent
// …
// …
if (intent.action == Intent.ACTION_SEARCH) { //
updateListFor(query)
When you run the app now, you should be able to search for
any food you want and get the relevant 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.
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res
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>
<android.support.constraint.ConstraintLayout …>
<FrameLayout
android:id="@+id/mainView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<android.support.design.widget.BottomNavigationVi
</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 fragment). This fragment class must extend
android.support.v4.app.Fragment.
import android.content.Context
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.*
import com.example.nutrilicious.R
import com.example.nutrilicious.view.common.getViewMo
import com.example.nutrilicious.viewmodel.SearchViewM
super.onAttach(context)
searchViewModel = getViewModel(SearchViewModel::c
return inflater.inflate(R.layout.fragment_search,
super.onViewCreated(view, savedInstanceState)
return ViewModelProviders.of(this).get(modelClass.j
launch(NETWORK) { // …
withContext(UI) {
adapter = SearchListAdapter(emptyList())
layoutManager = LinearLayoutManager(context)
addItemDecoration(DividerItemDecoration(
context, LinearLayoutManager.VERTICAL
))
setHasFixedSize(true)
// …
swipeRefresh.setOnRefreshListener {
// …
import android.support.v7.app.AppCompatActivity
// …
fun AppCompatActivity.replaceFragment(viewGroupId: In
supportFragmentManager.beginTransaction()
.commit()
import com.example.nutrilicious.view.common.replaceFr
// …
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
searchFragment = SearchFragment()
replaceFragment(R.id.mainView, searchFragment) /
navigation.setOnNavigationItemSelectedListener(na
import android.support.annotation.IdRes
// …
fun AppCompatActivity.addFragmentToState(
fragment: Fragment,
tag: String
) {
supportFragmentManager.beginTransaction()
.commit()
import com.example.nutrilicious.view.common.*
// …
// …
private fun recoverOrBuildSearchFragment() {
.findFragmentByTag(SEARCH_FRAGMENT_TAG) as? S
searchFragment = SearchFragment()
addFragmentToState(R.id.mainView, searchFragment,
// …
replaceFragment(R.id.mainView, searchFragment)
if (intent.action == Intent.ACTION_SEARCH) {
lastSearch = searchTerm
withContext(UI) {
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.
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res
xmlns:app="http://schemas.android.com/apk/res-aut
app:layout_behavior="@string/appbar_scrolling_vie
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/tvH
</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.
Listing 8.67 Resources for the Layout
Click here to view code image
// In res/values/dimens.xml
<dimen name="huge_font_size">22sp</dimen>
// In res/values/strings.xml
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 fragment uses
hard-coded sample data because users cannot select favorites
yet.
Listing 8.68 Implementing the Favorites Fragment
Click here to view code image
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_favori
class FavoritesFragment : Fragment() {
container: ViewGroup?,
savedInstanceState: Bundle
return inflater.inflate(R.layout.fragment_favorit
super.onViewCreated(view, savedInstanceState)
setUpRecyclerView()
adapter = SearchListAdapter(sampleData())
layoutManager = LinearLayoutManager(context)
addItemDecoration(DividerItemDecoration(
context, LinearLayoutManager.VERTICAL
))
setHasFixedSize(true)
import android.support.v7.widget.*
import com.example.nutrilicious.model.Food
// …
// …
companion object {
with(rv) {
adapter = SearchListAdapter(list)
layoutManager = LinearLayoutManager(context)
addItemDecoration(DividerItemDecoration(
context, LinearLayoutManager.VERTICAL
))
setHasFixedSize(true)
// In FavoritesFragment.kt
MainActivity.setUpRecyclerView(rvFavorites, sampleD
// In SearchFragment.kt
MainActivity.setUpRecyclerView(rvFoods)
when (it.itemId) {
R.id.navigation_home -> {
replaceFragment(R.id.mainView, searchFragment)
return@OnNavigationItemSelectedListener true
R.id.navigation_my_foods -> {
replaceFragment(R.id.mainView, FavoritesFragmen
return@OnNavigationItemSelectedListener true
false
import com.example.nutrilicious.model.Food
class SearchFragment : Fragment() {
// …
// …
// …
// …
launch(NETWORK) {
withContext(UI) { … }
Create the DAOs that offer all desired operations to access the database.
Implement an abstract subclass of RoomDatabase that provides the
DAOs.
import android.arch.persistence.room.*
// …
) { … }
import android.arch.lifecycle.LiveData
import android.arch.persistence.room.*
import android.arch.persistence.room.OnConflictStrate
import com.example.nutrilicious.model.Food
@Dao
interface FavoritesDao {
@Delete
import android.arch.persistence.room.*
import android.content.Context
import com.example.nutrilicious.model.Food
companion object {
return INSTANCE!!
.databaseBuilder(ctx, AppDatabase::class.java
.build()
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.
Listing 8.76 The View Model to Access Favorite Foods
Click here to view code image
import android.app.Application
import android.arch.lifecycle.*
import com.example.nutrilicious.data.db.*
import com.example.nutrilicious.model.Food
import kotlinx.coroutines.*
dao.loadAll()
import kotlinx.coroutines.newSingleThreadContext
class SearchListAdapter( // …
) : … {
// …
ivStar.setOnClickListener { onStarClick(food, t
// …
with(rv) {
// …
SearchListAdapter(items,
onStarClick = { food, layoutPosition -> //
toggleFavorite(food)
rv.adapter.notifyItemChanged(layoutPositi
})
import com.example.nutrilicious.viewmodel.FavoritesVi
// …
// …
favoritesViewModel = getViewModel(FavoritesViewMo
// …
private fun toggleFavorite(food: Food) {
if (wasFavoriteBefore) {
favoritesViewModel.delete(food)
} else {
favoritesViewModel.add(food)
import android.widget.Toast
// …
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.
import android.content.Context
import com.example.nutrilicious.view.common.getViewMo
import com.example.nutrilicious.viewmodel.FavoritesVi
import kotlinx.coroutines.android.UI
import kotlinx.coroutines.launch
import android.arch.lifecycle.Observer
// …
super.onAttach(context)
favoritesViewModel = getViewModel(FavoritesViewMo
}
// …
// …
observeFavorites()
favorites.observe(this@FavoritesFragment, Observe
foods?.let {
})
import com.example.nutrilicious.viewmodel.FavoritesVi
// …
// …
// …
favoritesViewModel = getViewModel(FavoritesViewMo
// …
lastSearch = searchTerm
swipeRefresh?.isRefreshing = true
launch {
.onEach { if (favoritesIds.contains(it.id))
lastResults = foods
withContext(UI) { … }
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
9
Reports API, 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
@GET("V2/reports?format=json")
fun getDetails(
): Call<DetailsWrapper<DetailsDto>>
"foods": [
"food": {
"sr": "Legacy",
"type": "b",
"ndbno": "09070",
"manu": "",
"ru": "g"
},
"derivation": "NONE",
"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).
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
class FoodsWrapper<T> {
@JsonClass(generateAdapter = true)
class FoodWrapper<T> {
}
typealias DetailsWrapper<T> = FoodsWrapper<FoodWrappe
@JsonClass(generateAdapter = true)
init {
@JsonClass(generateAdapter = true)
@JsonClass(generateAdapter = true)
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.
Listing 8.88 Domain Classes for Food Details
Click here to view code image
import com.example.nutrilicious.data.network.dto.*
) {
dto.desc.name,
dto.nutrients.map(::Nutrient)
) {
dto.nutrient_id!!,
dto.detailsId!!,
dto.name,
dto.value,
dto.unit,
NutrientType.valueOf(dto.group.toUpperCase())
}
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
constructors map the DTOs to these domain classes. In case a
late-initialized property was not populated 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.
import com.example.nutrilicious.data.network.*
import kotlinx.coroutines.launch
// …
launch(NETWORK) { usdaApi.getDetails("09070").exe
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.
Listing 8.90 Horizontal Divider Layout
Click here to view code image
<View xmlns:android="http://schemas.android.com/apk/r
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_margi
android:background="?android:attr/listDivider" />
<dimen name="divider_height">2dp</dimen>
<dimen name="divider_minheight">1px</dimen>
<dimen name="divider_margin">5dp</dimen>
<ScrollView xmlns:android="http://schemas.android.com
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.FoodDetailsActivi
<TextView
android:id="@+id/tvFoodName"
android:layout_width="match_parent"
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_
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_
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"
android:textColor="@android:color/darker_
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_
android:textSize="@dimen/medium_font_size
<TextView
android:id="@+id/tvLipids"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
<string name="proximates">Proximates</string>
<string name="minerals">Minerals</string>
<string name="vitamins">Vitamins</string>
<string name="lipids">Lipids</string>
) : RecyclerView.Adapter<ViewHolder>() {
// …
// …
containerView.setOnClickListener { onItemClick(
import com.example.nutrilicious.view.details.DetailsA
// …
SearchListAdapter(items,
onItemClick = { startDetailsActivity(it) },
onStarClick = { … }
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
Click here to view code image
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
request.execute().body()?.foods?.get(0)?.food /
} ?: 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 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.
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import com.example.nutrilicious.R
import com.example.nutrilicious.view.common.getViewMo
import com.example.nutrilicious.viewmodel.DetailsView
// …
detailsViewModel = getViewModel(DetailsViewModel:
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
Click here to view code image
import kotlinx.coroutines.android.UI
import kotlinx.coroutines.*
// …
// …
// …
updateUiWith(foodId)
if (foodId.isBlank()) return
launch {
withContext(UI) { bindUi(details) }
import com.example.nutrilicious.model.*
import kotlinx.android.synthetic.main.activity_detail
// …
// …
if (details != null) {
} else {
tvFoodName.text = getString(R.string.no_data)
"$displayName: $amountPer100g$unit"
}
}
<application …>
<activity android:name=".view.details.DetailsActi
android:parentActivityName=".view.main.MainAc
<meta-data
android:name="android.support.PARENT_ACTI
android:value=".view.main.MainActivity" /
</activity>
</application>
import android.arch.persistence.room.*
// …
@Entity(tableName = "details")
@TypeConverters(NutrientListConverter::class) // Is
// …
) { constructor(dto: DetailsDto) : this(…) }
@TypeConverters(NutrientTypeConverter::class)
import android.arch.persistence.room.TypeConverter
import com.example.nutrilicious.model.*
import com.squareup.moshi.*
class NutrientListConverter {
List::class.java, Nutrient::class.java
= adapter.fromJson(json) ?: emptyList()
class NutrientTypeConverter {
@TypeConverter
@TypeConverter
}
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.
import android.arch.persistence.room.*
import com.example.nutrilicious.model.FoodDetails
@Dao
interface DetailsDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
import com.example.nutrilicious.model.FoodDetails
// …
// …
import com.example.nutrilicious.BuildConfig
// …
.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.
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
?: withContext(NETWORK) { fetchDetailsFromApi
request.execute().body()?.foods?.get(0)?.food
} ?: return null
return FoodDetails(detailsDto)
import com.example.nutrilicious.data.DetailsRepositor
// …
As you can see, the view model now simply delegates its task
to the repository. Because the repository 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.
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
12
other code, you can find this on GitHub to copy and paste.
12. https://github.com/petersommerhoff/nutrilicious-
app/blob/master/12_AddingRdisForActionableData/app/src/main/java/com/peterso
mmerhoff/nutrilicious/model/RDI.kt
import com.example.nutrilicious.model.WeightUnit.*
@TypeConverters(NutrientTypeConverter::class)
// …,
// …
) {
// …,
Amount(dto.value.toDouble(), WeightUnit.fromStr
// …
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.
companion object {
The amount and unit are now extracted from the Amount
object. Additionally, the RDI percentage 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.
// …
withContext(UI) {
swipeRefresh?.isRefreshing = false
}
}
import android.support.design.widget.Snackbar
import android.view.View
fun Fragment.snackbar(
<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>
</ScrollView>
import android.view.View
// …
// …
private fun updateUiWith(foodId: String) {
if (foodId.isBlank()) return
launch {
withContext(UI) {
bindUi(details)
if (isLoading) {
content.visibility = View.GONE
progress.visibility = View.VISIBLE
} else {
progress.visibility = View.GONE
content.visibility = View.VISIBLE
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 functions. 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
Jos
Men build too many walls and not enough bridges.
eph
Fort Newton
INTRODUCING DSLS
1
DSLs are by no means new; research goes back decades
and is only increasing in recent times, from language
2 3
modeling in general to DSLs in particular. Today, DSLs
are pervasive in software development. This section
explains the term and discusses how DSLs can help
improve your code.
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
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
4
DSLs already, such as SQL, LaTeX, or regular
expressions. These are in contrast to general-purpose
5 6
languages like Kotlin, Java, C++, Python, and others.
4. https://www.latex-project.org/
5. http://www.stroustrup.com/C++.html
6. https://www.python.org/
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.
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.
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
10
DSL must involve domain experts. 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).
10. http://www.se-rwth.de/publications/Design-Guidelines-for-Domain-
Specific-Languages.pdf
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.
username = "johndoe"
address {
number = 42
postCode = "12345"
user.init()
return user
}
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
Click here to view code image
import java.time.LocalDate
address = Address().apply(init)
address {
username = "this-should-not-work"
user {
address {
import java.time.LocalDate
class UserBuilder {
address = AddressBuilder().apply(init).build()
class AddressBuilder {
var number = -1
if (notReady())
}
private fun notReady()
Nesting Deeper
The current DSL allows adding an arbitrary number of
address blocks, but each one would override 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.
class UserBuilder {
// …
addresses.add(AddressBuilder().apply(init).build(
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
Click here to view code image
user {
username = "johndoe"
number = 42
postCode = "12345"
address {
number = 1
postCode = "54321"
city = "York"
class UserBuilder {
// …
add(AddressBuilder().apply(init).build())
}
}
addresses.addAll(Addresses().apply(init))
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
13
with a React DSL used internally.
13. Source: “Create Your Own DSL in Kotlin” by Victor Kropp
(https://youtu.be/tZIRovCbYM8)
Introducing @DslMarker
This small DSL is now mostly finished, but the problems
of arbitrary nesting and accessing properties 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
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 thirdparty class. Instead, you can annotate the lambda receiver:
= UserBuilder().apply(init).build()
@DslMarker
@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE) // Can be used
@Retention(AnnotationRetention.SOURCE)
user {
// …
addresses {
address {
// …
city = usercity
}
address {
// …
city = usercity
user {
username = "johndoe"
// …
@UserDsl
class UserBuilder {
// …
// …
UserBuilder().apply(init).build()
DSL layouts are more reusable; with XML, you would usually at least
have to adjust the element IDs.
// …
super.onCreate(savedInstanceState)
setContentView(createView()) // No inflating of
viewModel = getViewModel(TodoViewModel::class)
orientation = LinearLayout.VERTICAL
hint = getString(R.string.enter_new_todo)
textAppearance = android.R.style.TextAppearance
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
text = getString(R.string.add_to_do)
textAppearance = android.R.style.TextAppearance
layoutParams = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
setOnClickListener {
launch(DB) { viewModel.add(TodoItem(newTodo))
finish()
}
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.
Listing 9.20 Anko Metadependency
Click here to view code image
implementation "org.jetbrains.anko:anko:$anko_version
implementation "org.jetbrains.anko:anko-sdk25-corouti
verticalLayout {
button {
verticalLayout {
margin = dip(5)
verticalLayout {
// …
super.onCreate(savedInstanceState)
viewModel = getViewModel(TodoViewModel::class)
}
hintResource = R.string.enter_new_todo
textAppearance = android.R.style.TextAppearance
margin = dip(16)
textAppearance = android.R.style.TextAppearance
gravity = Gravity.CENTER_HORIZONTAL
launch(DB) { viewModel.add(TodoItem(newTodo)) }
finish()
// …
super.onCreate(savedInstanceState)
setContentView(AddTodoActivityUi().createView(Ank
viewModel = getViewModel(TodoViewModel::class)
verticalLayout {
hintResource = R.string.enter_new_todo
textAppearance = android.R.style.TextAppear
margin = dip(16)
}
button(R.string.add_to_do) {
textAppearance = android.R.style.TextAppear
gravity = Gravity.CENTER_HORIZONTAL
}.setOnClickListener {
launch(DB) { viewModel.add(TodoItem(newTodo
finish()
import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
class SquareFrameLayout(
context: Context,
defStyleAttr: Int = 0
super.onMeasure(widthMeasureSpec, widthMeasureSpe
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
Click here to view code image
import android.view.ViewManager
import org.jetbrains.anko.custom.ankoView
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
16
Gradle Kotlin DSL 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
17
the ability to use all of Kotlin’s language features, thus
making it easier to write build scripts from scratch.
16. https://github.com/gradle/kotlin-dsl
17. https://blog.gradle.org/kotlin-meets-gradle
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.
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")
buildscript {
repositories {
google()
jcenter()
dependencies {
classpath 'com.android.tools.build:gradle:3.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plu
}
}
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-plu
allprojects {
repositories {
jcenter()
google()
Delete Task
delete rootProject.buildDir
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.
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
18
plugin search. Personally, I prefer the consistent look of
using only id.
18. https://plugins.gradle.org/search?term=org.jetbrains.kotlin
Android
android {
compileSdkVersion(27)
defaultConfig {
applicationId = "com.example.nutrilicious"
minSdkVersion(19)
targetSdkVersion(27)
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "android.support.test
buildTypes {
getByName("release") {
isMinifyEnabled = false
proguardFiles("proguard-rules.pro")
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-
// …
implementation("com.squareup.moshi:moshi:$moshi_ver
kapt("com.squareup.moshi:moshi-kotlin-codegen:$mosh
// …
testImplementation("junit:junit:4.12")
androidTestImplementation("com.android.support.test
androidTestImplementation("com.android.support.test
androidExtensions {
configure(delegateClosureOf<AndroidExtensionsExtens
isExperimental = true
})
object BuildPlugins {
object Android {
val minSdkVersion = 19
val targetSdkVersion = 27
val compileSdkVersion = 27
val versionCode = 1
object Libs {
plugins {
android {
// …
targetSdkVersion(Android.targetSdkVersion) // Uses
versionCode = Android.versionCode
// …
dependencies {
// …
implementation(Libs.moshi) // Uses
kapt(Libs.moshi_codegen)
// …
SUMMARY
You can now create your own simple Kotlin DSLs from
scratch by combining higher-order functions, 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.
10
Migrating to Kotlin
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).
Note
Think about each of these general points in terms of migrating from Java to
Kotlin to contemplate benefits and risks it may have in your opinion.
Is the team interested in using (and learning) the new tool or technology?
Does it integrate well with the current technology stack and tooling?
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.
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
9
lead the adoption at Square.
9. https://docs.google.com/document/d/1ReS3ep-
hjxWA8kZi0YqDbEhCqTt29hG8P44aA9W0DM8/
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.
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.
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
11
community.
11. https://kotlinlang.org/community/
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.
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, 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.
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.
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
12
the Android Developers website 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 relatively easy to migrate back to Java.
12. https://developer.android.com/kotlin/get-started#kotlin
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.
} else {
person?.let {
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
13
rewrite your JUnit 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.
13. https://junit.org/
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.
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.
You can collect data (ideally with the same technology stack) to
discover potential problems ahead of time. To measure build times, you
14
can use the Gradle Profiler.
14. https://github.com/gradle/gradle-profiler
You will come across issues early on—and before using Kotlin in
production. Whether this is not being able to run individual test cases
15 16
with Spek (a testing framework for Kotlin), Mockito not always
17
playing smoothly with Kotlin (MockK is a good alternative), or the
tendency of tooling to be behind a bit.
15. https://spekframework.org/
16. https://site.mockito.org/
17. http://mockk.io/
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.
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.
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.
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.
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.
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.
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.
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.
Pitch an evaluation of Kotlin at your company, for instance, with a
document highlighting features, benefits, and compatibility with the
company’s technology stack.
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.
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.
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.
COMMUNITY
Kotlin Slack Channel: http://slack.kotlinlang.org/
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 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
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/1ReS3ephjxWA8kZi0Yq
DbEhCqTt29hG8P44aA9W0DM8/edit?usp=sharing
TESTING
Spek: https://spekframework.org/
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.
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.
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
? (question mark)
elvis operator (?:), 30–31
safe call operator (?), 29–30
A
abstract classes, 87–88
abstract keyword, 87
accessing collections, 47
actionable data (Nutrilicious app), 307–311
activities, binding coroutines to, 177–179
actor function, 187–188
actor model for concurrent programming, 155, 186–192
conflated channels, 188–189
custom message types, 191–192
history of, 192
multiple actors on same channel, 190–191
multiple channels, 189–190
producers, 191
ReceiveChannel, 188
rendezvous channels, 190
SendChannel, 187–188
simple example, 186–187
unlimited channels, 189
adapters, RecyclerView
Kudoo app, 215–219
Nutrilicious app, 246–248
addition (+) operator, 28
address function, 322, 323
AddressBuilder class, 322
addresses, adding to users
multiple addresses, 323–325
single addresses, 319–320
addresses block, 323–325
AddTodoActivity, 233–237
creating programmatically, 328–329
migrating to Anko layouts, 332
algebraic data types, 100
ALGOL W, 29
aliases, type, 107
allprojects block (Gradle Kotlin DSL), 337
also function, 58–59
Amount class, 307–311
ampersand (&), 15
and (&&) operator, 15
Android, 125
android block (Gradle Kotlin DSL), 338–339
Android KTX, 7, 230
Android layouts
Anko
Anko dependencies, 329–330
custom views, 334–335
layout parameters, 330–331
migrating Kudoo’s AddTodoActivity to, 332
modularizing, 333
previewing, 331
simple layout example, 330
XML layouts versus, 335
creating programmatically, 328–329
overview of, 328
Android PacKage (APK), 258
Android Runtime (ART), 5–6
Android SDK (software development kit), 124
Android Search Interface, 265
Android Studio
autocompletion, 28
compiled bytecode, viewing, 134–135
decompiled Java code, viewing, 134–135
Device File Explorer, 227
overview of, 7, 8, 205
Sample Data Directory, 258
Android Virtual Device (AVD), 212
android-apt plugin, 208–209
AndroidExtensionsExtension, 340
Anko library, 6
dependencies, 329–330
layouts
custom views, 334–335
migrating Kudoo’s AddTodoActivity to, 332
modularizing, 333
parameters, 330–331
previewing, 331
simple example, 330
XML layouts versus, 335
ankoView function, 334–335
annotation processors, 208–209
annotations
annotation processors, 208–209
@Database, 223–224
@DslMarker, 325–326
@Entity, 222
@GET, 255
@JvmField, 135–136, 150
@JvmName, 134, 137, 150
@JvmOverloads, 141, 150
@JvmStatic, 140, 150
@JvmSuppressWildcards, 147–148
nullability, 128–129
@Query, 255
@RestrictsSuspension, 185
@SerializedName, 261
@Throws, 145–146, 149
@TypeConverter, 303
@TypeConverters, 303
@UserDsl, 325
Any type, 88
APIs (application programming interfaces)
Android KTX, 7
asynchronous APIs, wrapping, 195–197
Collections. See collections
Search API, 255–256, 258–260
USDA Nutrition API, fetching data from
API requests, performing, 256–257
Gradle dependencies for, 250–251
Retrofit interface, 251–256
AppDatabase class, 223–225, 282
apply function, 54–55, 329
apps
Kudoo
AddTodoActivity, 233–237
to-do items, adding, 233–237
to-do items, checking off, 237–239
event handlers, 237–239
finished app, 210
migrating to Anko layouts, 332
project setup, 210–212
RecyclerView, 212–221
Room database, 221–233
Nutrilicious
data mapping, 257–262
DetailsActivity, 293–301
empty search results, indicating, 311–312
Favorites fragment, 276–280
finished app, 241
food details, storing in database, 302–306
migrating to Gradle Kotlin DSL, 336–343
progress indicator, 312–313
project setup, 242–243
RDIs (recommended daily intake), adding, 307–311
RecyclerView, 243–250, 261–262
Room database, 280–288
search interface, 265–268
SearchFragment, 268–275
SearchViewModel class, 262–264
USDA Food Reports API, fetching data from, 288–293
USDA Nutrition API, fetching data from, 250–257
Archivable interface, 86
arguments, 22
arithmetic operators, table of, 28
Array type, 127
arrayListOf, 46
arrays
creating, 128
invariance of, 111
arrow (->), 16
ART (Android Runtime), 5–6
as operator, 90
assigning lambda expressions, 40
assignment operators, 28
associate function, 49
associating collections, 49
asterisk (*)
multiplication operator (*), 28
timesAssign operator (*=), 28
async keyword, 157, 169–172
asynchrony
with async-await pattern, 157
asynchronous APIs, wrapping, 195–197
with callbacks, 155–156
with coroutines, 158–159, 160
with futures, 156–157
overview of, 152–153, 155–157
autoconverted code, adjusting, 355–356
auto-generated Gradle configuration, 205–207
AutoValue, 348
AVD (Android Virtual Device), 212
await keyword, 157
B
backing fields, 72
backtick (`), 130
binding coroutines, 177–179
bindUi function, 309
Bloch, Joshua, 88
blocks, 336
Boolean data type, 13
Bossidy, Larry, 69
bounded type parameters, 118–119
Buck, 7
build scripts
Gradle Kotlin DSL, migration to, 335–336
benefits of, 342
buildSrc directory, 340–342
drawbacks of, 342–343
Gradle settings, 336
module build script, 338–340
root build script, 336–338
overview of, 205–207
builders, coroutine
actor, 186–191
async, 169–172
contexts, 172
accessing elements of, 181
coroutine dispatchers, 172–177
jobs, 177–181
future, 182
immutability through, 320–322
launch, 164–169
overview of, 163
parameters of, 181–183
runblocking, 163–164
withContext, 176
build.gradle file, 205–207
buildIterator function, 185
BuildPlugins object, 342
buildscript block (Gradle Kotlin DSL), 336–337
buildSequence function, 184
buildSrc directory, 340–342
business, Kotlin for
advantages of, 7–8
companies incorporating Kotlin, 8
platform compatibility, 9
Butter Knife, 348
buy-in for migration, gaining, 347–348
Byte data type, 13
C
C language, 9
C++316
callback hell, 155–156
callbacks, 155–156
calling
functions, 22
chaining calls, 52–53
extension functions, 25
higher-order functions, 42
infix functions, 27
lambas as arguments to, 42
getters/setters, 124–125
Java code from Kotlin
considerations for, 132–133
getters, calling, 124–125
identifiers, escaping, 130
Kotlin-friendly Java code, writing, 149–150
nullability, 125–129
operators, 131
overview of, 123–124
SAM (single abstract method) types, 131–132
setters, calling, 124–125
vararg methods, calling, 130–131
Kotlin code from Java, 133. See also visibilities
data classes, 142–143
extensions, 137–138
file-level declarations, 136–137
Java-friendly Kotlin code, writing, 149–150
method overloading, 141
properties, calling, 133–135
properties, exposing as fields, 135–136
sealed classes, 142–143
static fields, 138–139
static methods, 140
methods, 80–81
operators, 27
casting types
ClassCastException, 90
nullable types, 90
smart casts, 30, 90–91
catch keyword, 33–35
catching exceptions, 33–35
Ceylon, 4
chaining function calls, 52–53
channels
conflated, 188–189
multiple, 189–190
multiple actors on same channel, 190–191
overview of, 186–192
producers, 191
ReceiveChannel, 188
rendezvous, 190
SendChannel, 187–188
simple example, 186–187
unlimited, 189
Char data type, 13
checked exceptions, 35–36
child coroutines, 177–178
class keyword, 69
ClassCastException, 90
classes
abstract, 87–88
AddressBuilder, 322
Amount, 307–311
AppDatabase, 223–225, 282
constructors
primary, 70, 82–83
secondary, 84
data classes
calling from Java, 142–143
declaring, 94–95
inheritance, 96
declaring, 69
top-level declarations, 93–94
visibility modifiers, 92–93
delegated implementations, 79–80
DetailsDto, 291
DetailsRepository, 305–306
DetailsViewModel, 298–300, 306
domain classes, mapping data to
DTO-to-model mapping, 260–262
JSON-to-DTO mapping, 258–260
overview of, 257–258
enum, 96–98
FavoritesViewModel, 282–283, 286–287
Food, 246, 280–281
FoodDetails, 291–292, 302
FragmentActivity, 230
generic, 106–107
immutable, 320–321
inheritance
overriding rules for, 89
overview of, 84–85
inner, 82
KClass, 143–144
methods
calling, 80–81
declaring, 80–81
dynamic dispatch, 80–81
extension, 81
getters, 71
overriding, 80–81
setters, 71
nested, 82
Nutrient, 308
NutrientListConverter, 302–303
NutrientTypeConverter, 303
open, 88–89
polymorphism, 80–81
properties
adding, 70–71
backing fields, 72
delegated, 74–79
fields compared to, 71
late-initialized, 72–73
lazy, 75–76
observable, 76–77
sealed
calling from Java, 142–143
declaring, 98–100
SearchListAdapter, 246–248, 283–284
SearchViewModel, 262–264
TodoItem, 222
TodoItemDao, 222–223
types versus, 110
User
adding addresses to, 319–320, 323–325
declaring, 319
UserBuilder, 321
ViewHolder, 216–217
ViewModel
Kudoo app, 227–230
Nutrilicious app, 262–264
class-local extensions, calling, 138
Clojure, 4
closed-by-default principle, 85, 88
ClosedSendChannelException, 187
COBOL, 316
code base, integrating Kotlin into, 351
pet projects, 353–354
production code, 352–353
test code, 351–352
collections
accessing, 47
associating, 49
editing, 47
filtering, 47–48
folding, 51–52
grouping, 49
helper functions for, 46
instantiating, 46
Kotlin versus Java, 45–46
mapping, 48
minimum, maximum, and sum calculations, 49–50
sorting, 50–51
variance of, 111–112
Collections API. See collections
colon (:), 39
combining higher-order functions, 59–60
companion keyword, 103–105
companion objects, 103–105
compiled bytecode, viewing, 134–135
compile-time polymorphism, 108
CompletableFuture, 152, 182–183
complex objects, building with DSLs (domain-specific
languages), 318–320
concise code, 4
concurrency. See also coroutines
actor model for, 186–192
conflated channels, 188–189
custom message types, 191–192
history of, 192
multiple actors on same channel, 190–191
multiple channels, 189–190
producers, 191
ReceiveChannel, 188
rendezvous channels, 190
SendChannel, 187–188
simple example, 186–187
unlimited channels, 189
challenges of, 153–155
generators, 184–185
overview of, 151–153
solutions to, 155–157
transformation between styles of, 193
concurrent execution, 152–153
conditional expressions
if keyword, 15–19
when keyword, 15–19
configuration. See also Kudoo app; Nutrilicious app
AVD (Android Virtual Device), 212
Grade
annotation processors, 208–209
auto-generated configuration, 205–207
dependencies, adding, 207–208
conflated channels, 188–189
const keyword, 135
constructor keyword, 83
constructors
primary, 70, 82–83
secondary, 84
contains operator, 28
contexts, coroutine, 172
accessing elements of, 181
coroutine dispatchers, 172–177
jobs, 177–181
Continuation Passing Style (CPS), 156
continuations, 156
contravariance, 112
conversions, SAM (single abstract method), 131–132
Convert Java File to Kotlin File command (Code menu),
209, 355
converter, Java-to-Kotlin, 8, 355
Conway, Melvin E.192
cooperative multitasking, 157–158
coroutine builders
actor, 186–191
async, 169–172
contexts, 172–181
future, 182
immutability through, 320–322
launch, 164–169
overview of, 163
parameters of, 181–183
runblocking, 163–164
withContext, 176
coroutine dispatchers (Nutrilicious app), 283
CoroutineContext, 172
CoroutineDispatcher, 172–177
Job object, 177–181
CoroutineDispatcher, 172–177
CoroutineExceptionHandler, 180
coroutines
asynchrony with, 158–159, 160, 195–197
binding to activities, 177–179
builders
actor, 186–191
async, 169–172
contexts, 172–181
future, 182
immutability through, 320–322
launch, 164–169
overview of, 163
parameters of, 181–183
runblocking, 163–164
withContext, 176
child, 177–178
contexts, 172
accessing elements of, 181
coroutine dispatchers, 172–177
jobs, 177–181
debugging, 193–195
generator implementation with, 184–185
Gradle dependencies for, 256
history of, 192
interoperability with Java, 197–198
lazy start, 182
overview of, 157–158
parameters of, 181–183
setup for, 158
suspending functions, 159–163
wrappers for, 182
CoroutineStart, 181–182
covariance, 110–112
Covey, Stephen, 123
CPS (Continuation Passing Style), 156
CPU-bound operations, 165
crossinline keyword, 44
custom message types, actors with, 191–192
custom views, Anko layouts, 334–335
D
Dalvik virtual machine, 5–6
DAOs (data access objects)
FavoritesDao interface, 281
TodoItemDao class, 222–223
data classes
calling from Java, 142–143
declaring, 94–95
inheritance, 96
data keyword, 94–95
data mapping (Nutrilicious app)
DTOs to models, 260–262
JSON to DTOs, 258–260
overview of, 257–258
data transfer objects. See DTOs (data transfer objects)
data types. See also nullability
algebraic, 100
aliases, 107
Any, 88
casting
ClassCastException, 90
nullable types, 90
smart casts, 30, 90–91
checking, 89
classes versus, 110
function types, 39
generic type parameters, 105–106
mapped, 125
mapping, 14
nullable
casting, 90
elvis operator (?:), 30–31
overview of, 29
safe call operator (?), 29–30
unsafe call operator (!!), 31, 355
parameters, 40–41, 105–106
platform types, 126–128
table of, 13–14
type inference, 14–15
variance
bounded type parameters, 118–119
contravariance, 112
covariance, 110–112
declaration-site, 113–116
star projections, 119–121
use-site, 116–118
@Database annotation, 223–224
databases, 6
Kudoo Room database
AppDatabase class, 223–225
Gradle dependencies for, 221–222, 225–226
LiveData class, 230–233
MainActivity, 226–227
TodoItem class, 222
TodoItemDao class, 222–223
ViewModel class, 227–230
Nutrilicious Room database
AppDatabase class, 282
coroutine dispatchers, 283
FavoritesDao interface, 281
FavoritesViewModel class, 282–283, 286–287
Food class, 280–281
SearchFragment, 287–288
SearchListAdapter class, 283–284
storing food details in, 302–306
toggleFavorite function, 285
deadlock, 153
debugging coroutines, 193–195
dec operator (--), 28
declarations
classes, 69
data, 94–95
enum, 96–97
generic, 106–107
sealed, 98–100
top-level declarations, 93–94
User, 319
visibility modifiers, 92–93
file-level, 136–137
functions, 21
extension, 25
generic, 107–108
infix, 27
main, 22–23
suspending, 159
generators, 184
getters/setters, 71
methods, 80–81
objects, 103–105
operators, 27
variables, 12–13
mutable, 12
read-only, 12
declaration-site variance, 113–116, 146–148
decompiled Java code, viewing, 134–135
deep nesting, 323–325
DefaultDispatcher object, 165
delegated implementations, 79–80
delegated properties
implementation of, 74–75
lazy properties, 75–76
maps, 78–79
observable properties, 76–77
syntax of, 74
dependencies
adding, 207–208
Anko, 329–330
Descartes, René, 37
design patterns, Strategy, 39
DetailsActivity
click handler, 297
data display, 300–301
DetailsViewModel class, 298–300
FOOD_ID_EXTRA identifier, 298
item click listener, 296–297
layout, 293–296
navigation, 301
string resources for headline, 296
DetailsDto class, 291
DetailsRepository class, 305–306
DetailsViewModel class, 298–300, 306
development of Kotlin, 3–4
Device File Explorer, 227
DiffUtil, 233
dimension resources, 214
dining philosophers analogy, 153–155
direct style, 156
directories
buildSrc, 340–342
Sample Data Directory, 258
dispatch receivers, 81
dispatchers, coroutine, 172–177, 283
division (/) operator, 28
to-do list app. See Kudoo app
domain classes, mapping data to
DTO-to-model mapping, 260–262
JSON-to-DTO mapping, 258–260
overview of, 257–258
domain-specific languages. See DSLs (domain-specific
languages)
Double data type, 13
doubleArrayOf method, 128
do-while loops, 19
drop function, 64–65
@DslMarker annotation, 325–326
DSLs (domain-specific languages)
benefits of, 316–317
creating
complex objects, building, 318–320
deep nesting, 323–325
@DslMarker annotation, 325–326
immutability through builders, 320–322
simple objects, building, 318
definition of, 315–316
drawbacks of, 317–318
embedded, 316
Gradle Kotlin DSL, migration to, 335–336
benefits of, 342
buildSrc directory, 340–342
drawbacks of, 342–343
Gradle settings, 336
module build script, 338–340
root build script, 336–338
history of, 315
language features, 326–328
layouts, 328
creating programmatically, 328–329
creating with Anko, 329–335
performance of, 327–328
readability of, 323–327
DTOs (data transfer objects)
DetailsDto, 291
mapping JSON data to, 258–260
mapping to models, 260–262
wrappers for, 290
dynamic dispatch, 80–81
E
eager evaluation, 62, 65
Eclipse, 7
editing collections, 47
Effective Java (Bloch), 88
Elixir, 186
else keyword, 15
else-if keyword, 15
elvis operator (?:), 30–31
embedded DSLs (domain-specific languages), 316
empty search results, indicating, 311–312
@Entity annotation, 222
enum classes, 96–98, 309
enumerations, 96–98
equal sign (=)
assignment operators, 28
referential equality operator (===), 32
single-expression functions, 22
structural equality operator (==), 32
equality
checking, 32
floating point, 32–33
Erlang, 186
escaping Java identifiers, 130
evaluation, eager, 62, 65
evaluation, lazy
concept of, 62
definition of, 38
lazy sequences, 63–66
creating, 63–64
drop function, 64–65
performance of, 65–66
take function, 64–65
event handlers (Kudoo app), 237–239
Evernote, 8
exception handling
checked exceptions, 35–36
ClassCastException, 90
ClosedSendChannelException, 187
CoroutineExceptionHandler, 180
Java interoperability, 145–146
NullPointerException, 4, 8, 29
principles of, 33–35
suspending functions, 162
TimeoutCancellationException, 177
unchecked exceptions, 35–36
exclamation mark (!)
not (!) operator, 15
unsafe call operator (!!), 31, 355
execution, concurrent, 152–153
experimental extensions, enabling in Gradle Kotlin DSL,
340
expressions
conditional
if keyword, 15–19
when keyword, 15–19
eager evaluation, 62, 65
lambda
as arguments to higher-order functions, 42
assigning, 40
defining, 40
definition of, 37
implicit arguments, 41
with receivers, 60–61
type inference, 40–41
lazy evaluation
concept of, 62
definition of, 38
lazy sequences, 63–66
object, 101–102
extension functions
Android KTX, 230
creating, 25
importing, 26
scope, 26
static resolving of, 25–26
extension methods, 81
extension receivers, 81
extensions
AndroidExtensionsExtension, 340
calling from Java, 137–138
experimental, enabling in Gradle Kotlin DSL, 340
F
Favorites fragment, 276–280
FavoritesDao interface, 281
FavoritesViewModel class, 282–283, 286–287
fetchDetailsFromApi function, 306
fetching data
from USDA Food Reports API, 288–293
from USDA Nutrition API
API requests, performing, 256–257
Gradle dependencies for, 250–251
Retrofit interface, 251–256
fib function, 21–22
fields, 71
exposing properties as, 135–136
static, calling from Java, 138–139
file-level declarations, 136–137
filter function, 47–48
filtering collections, 47–48
Find Action command, 207
FindBugs, 125
findViewById function, 217–218
first-order functions, 39
Float data type, 13
Floating Point Arithmetic, 32
floating point equality, 32–33
flow control
conditional expressions
if keyword, 15–19
when keyword, 15–19
loops
do-while, 19
for, 20–21
while, 19
fold function, 51–52
folding collections, 51–52
food and nutrition app. See Nutrilicious app
Food class, 246, 280–281
food details, storing in database, 302–306
FOOD_ID_EXTRA identifier, 298
FoodDetails class, 291–292, 302
for loops, 20–21
Ford, Henry, 205
Fortran, 316
forward slash (/)
divAssign operator (/=), 28
division operator (/), 28
FragmentActivity class, 230
fragments
Favorites, 276–280
FragmentActivity class, 230
SearchFragment
adding to UI, 273
database connection, 287–288
fragment transactions, 272
getViewModel extension, 270–271
layout for, 268–269
MainActivity, 269
methods, 269–270
onCreate method, 274
properties, 272
recoverOrBuildSearchFragment method, 274
restoring, 274
search intents, 275
setUpSearchRecyclerView function, 271
storing in activity’s state, 273
swipe refresh, 275
SwipeRefreshLayout, 271–272
updateListFor function, 271
transactions, 272
frameworks for testing, 353
fromString method, 309
full migration, 350–351
fun keyword, 21, 25, 130
functional programming. See also functions
benefits of, 38–39
collections
accessing, 47
associating, 49
editing, 47
filtering, 47–48
folding, 51–52
grouping, 49
helper functions for, 46
instantiating, 46
Kotlin versus Java, 45–46
mapping, 48
minimum, maximum, and sum calculations, 49–50
sorting, 50–51
variance of, 111–112
lambda expressions
assigning, 40
defining, 40
definition of, 37
implicit arguments, 41
type inference, 40–41
lazy evaluation, 38
purpose of, 37–38
functions. See also lambda expressions; methods;
operators
actor, 187–188
address, 322, 323
also, 58–59
ankoView, 334–335
apply, 54–55, 329
arguments, 22
arrayListOf, 46
associate, 49
bindUi, 309
buildIterator, 185
buildSequence, 184
callbacks, 155–156
calling, 22, 52–53
chaining calls to, 52–53
declaring, 21
drop, 64–65
extension
creating, 25
importing, 26
scope, 26
static resolving of, 25–26
fetchDetailsFromApi, 306
fib, 21–22
filter, 47–48
findViewById, 217–218
first-order, 39
fold, 51–52
function types, 39
generic, 107–108
getDetails, 300–301, 306
getPercentOfRdi, 310
getViewModel, 264
groupBy, 49
hashMapOf, 46
hashSetOf, 46
higher-order, 37. See also specific function names
calling, 42
combining, 59–60
defining, 41
inlining, 42–43
lambas as arguments to, 42
non-local returns, 44–45
suspending functions and, 162–163
infix, 26–27, 323–327
inline, 145
lazy evaluation
concept of, 62
lazy sequences, 63–66
let, 53–54
linkedMapOf, 46
linkedSetOf, 46
listOf, 46
lparams, 330–331
main, 22–23
makeSection, 309
map, 48
mapOf, 46
maxBy, 49–50
minBy, 49–50
mutableListOf, 46
mutableMapOf, 46
mutableSetOf, 46
overloading, 24–25
parameters, 23–24
plus, 180
pointers to, 39
printAll, 119–121
receive, 187
reduce, 52
reduceRight, 52
renderNutrient, 301, 310
run, 56–58
send, 187
sendEmail, 24
setLoading, 313
setOf, 46
setUpSearchRecyclerView, 271
signatures, 21–22, 39, 144–145, 159
single-expression, 22
sorted, 50
sortedArray, 50
sortedBy, 50
sortedDescending, 50
sortedMapOf, 46
sortedSetOf, 46
sortedWith, 50
sumBy, 49–50
suspendCoroutine, 183
suspending, 157, 159–163, 164, 198–200
take, 64–65
toast, 330
toggleFavorite, 285
toSortedMap, 50
updateListFor, 271, 275, 311
updateUiWith, 313
use, 59
user, 318
verticalLayout, 330
with, 55–56
withContext, 176
withTimeout, 176
yield, 184
future coroutine builder, 182
futures, 152, 156, 255
G
generators, 184–185
generics
benefits of, 105
classes, 106–107
functions, 107–108
reification, 108–109
type parameters, 105–106
@GET annotation, 255
get method, 71
get operator, 28
getDetails function, 300–301, 306
getFoods method, 257
getItemCount method, 216, 218–219, 246
getPercentOfRdi function, 310
getters
calling, 124–125
declaring, 71
getViewModel extension, 270–271
getViewModel function, 264
Git, 212
GitHub, 158, 212, 242, 307
.gitignore file, 212
Go, 186
Google, support for Kotlin, 5
Gradle dependencies
Kudoo app, 221–222, 225–226
Nutrilicious app
architectural components, 262
Kotlin coroutines, 256
Moshi, 258
network and API access, 250–251
Gradle Kotlin DSL, migration to, 7, 335–336
benefits of, 342
buildSrc directory, 340–342
configuration
annotation processors, 208–209
auto-generated, 205–207
dependencies, adding, 207–208
drawbacks of, 342–343
Gradle settings, 336
Kudoo app, 221–222, 225–226
module build script
android block, 338–339
dependencies, 339–340
experimental features, 340
plugins block, 336–338
root build script
allprojects block, 337
buildscript block, 336–337
delete task, 337–338
support for, 346
Gradle Profiler, 353
groupBy function, 49
grouping collections, 49
Gson, 346
H
hashMapOf function, 46
hashSetOf function, 46
helper functions, for collections, 46
Hewitt, Carl, 186, 192
higher-order functions. See also specific function names
calling, 42
combining, 59–60
defining, 41
inlining, 42–43
lambas as arguments to, 42
non-local returns, 44–45
overview of, 37
suspending functions and, 162–163
Hoare, Tony, 29
HTTPLoggingInterceptor, 253
I
identifiers, escaping, 130
if keyword, 15–19
immutability
data classes, 320–321
through builders, 320–322
variables, 13
implementations, delegated, 79–80
implicit arguments, lambda expressions, 41
importing extension functions, 26
in operator, 28
inc operator (++), 28
indexed access operator, 47
inference, type, 14–15
infix functions, 26–27, 323–327
infix keyword, 26–27
inheritance
abstract classes, 87–88
closed-by-default principle, 85, 88
data classes, 96
interfaces, 85–86
open classes, 88–89
overriding rules for, 89
overview of, 84–85
initializing objects, 55
inline functions
calling from Java, 145
higher-order functions, 42–43
inline keyword, 42–43, 145
inner classes, 82
instantiating collections, 46
Int data type, 13
intArrayOf method, 128
integrating Kotlin into code base, 351
pet projects, 353–354
production code, 352–353
test code, 351–352
IntelliJ IDEA, 7, 8
autocompletion, 28
compiled bytecode, viewing, 134–135
decompiled Java code, viewing, 134–135
Interceptor interface, 254
interfaces
Archivable, 86
declaring, 92–93
FavoritesDao, 281
inheritance, 85–86
Interceptor, 254
internal keyword, 92, 94
Internet access, enabling, 256
interoperability with Java
best practices for, 149–150
Collections API, 45–46
CompletableFuture, 182–183
coroutines, 197–198
Java code, calling from Kotlin
considerations for, 132–133
getters, 124–125
identifiers, escaping, 130
Kotlin-friendly Java code, writing, 149–150
nullability, 125–129
operators, 131
overview of, 4, 123–124
SAM (single abstract method) types, 131–132
vararg methods, 130–131
Java-to-Kotlin converter, 209
Kotlin code, calling from Java, 133
data classes, 142–143
extensions, 137–138
file-level declarations, 136–137
Java-friendly Kotlin code, writing, 149–150
method overloading, 141
properties, calling, 133–135
properties, exposing as fields, 135–136
sealed classes, 142–143
static fields, 138–139
static methods, 140
variance, 115, 116
visibilities, mapping to Java
exception handling, 145–146
inline functions, 145
KClass, 143–144
Nothing type, 148
overview of, 143
signature clashes, 144–145
variant types, 146–148
interpolation, string, 20
iOS, 3
is operator, 89
it keyword, 61
item layout, Nutrilicious app, 244–246
iterator method, 124
J
Java code, calling from Kotlin
considerations for, 132–133
getters, 124–125
identifiers, escaping, 130
nullability
annotations for, 128–129
nullables and mapped types, 125
overview of, 125
platform types, 126–128
operators, 131
overview of, 4, 123–124
SAM (single abstract method) types, 131–132
vararg methods, 130–131
Java interoperability
best practices for, 149–150
Collections API, 45–46
CompletableFuture, 182–183
coroutines, 197–198
Java code, calling from Kotlin
considerations for, 132–133
getters, calling, 124–125
identifiers, escaping, 130
Kotlin-friendly Java code, writing, 149–150
nullability, 125–129
operators, 131
overview of, 123–124
SAM (single abstract method) types, 131–132
setters, calling, 124–125
vararg methods, calling, 130–131
Java-to-Kotlin converter, 8, 209, 355
Kotlin code, calling from Java, 133
data classes, 142–143
extensions, 137–138
file-level declarations, 136–137
Java-friendly Kotlin code, writing, 149–150
method overloading, 141
properties, calling, 133–135
properties, exposing as fields, 135–136
sealed classes, 142–143
static fields, 138–139
static methods, 140
Kotlin’s advantages over Java, 6, 9
overview of, 4
streams, 62
variance, 115, 116
visibilities, mapping to Java
exception handling, 145–146
inline functions, 145
KClass, 143–144
Nothing type, 148
overview of, 143
signature clashes, 144–145
variant types, 146–148
Java on Android, 5–6
Java Streams, 6
JavaFX, 6, 182
JavaScript, 3
Kotlin/JS, 9
Kotlin’s advantages over, 9
Java-to-Kotlin converter, 8, 209, 355
JetBrains, 3–4, 7, 125
Job object, 177–181
jobs, coroutine, 177–181
JSON data, mapping to DTOs (data transfer objects), 258–
260
JSR 305, 125
JUnit, 124, 352
JVM (Java virtual machine), 3, 5, 9
@JvmField annotation, 135–136, 150
@JvmName annotation, 134, 137, 150
@JvmOverloads annotation, 141, 150
@JvmStatic annotation, 140, 150
@JvmSuppressWildcards annotation, 147–148
K
kapt plugin, 208–209
kaptAndroidTest, 209
kaptTest, 209
KClass, 143–144
Keller, Gary, 151
keywords. See also coroutine builders; functions; methods
abstract, 87
async, 157, 169–172
await, 157
catch, 33–35
class, 69
companion, 103–105
const, 135
constructor, 83
crossinline, 44
data, 94–95
do-while, 19
else, 15
else-if, 15
for, 20–21
fun, 21, 25, 130
if, 15–19
infix, 26–27
inline, 42–43, 145
internal, 92, 94
it, 61
lateinit, 73
launch, 164–169
object, 101–102, 130
open, 87
override, 86, 104–105
private, 92, 94
protected, 92
public, 92, 94
reified, 109
runblocking, 163–164
sealed, 98–100
static, 104
super, 87
suspend, 159–163, 198–200
this, 61
throw, 33–35
try, 33–35
val, 12, 71, 83, 130
var, 12–13, 71, 83, 130
when, 15–19, 99–100
where, 118–119
while, 19
knowledge sharing, 348–349
Kotlin/JS, 9
Kotlin/JVM, 9
Kotlin/Native, 9
.kts file extension, 11–12
Kudoo app
AddTodoActivity, 233–237
creating programmatically, 328–329
migrating to Anko layouts, 332
to-do items, adding, 233–237
to-do items, checking off, 237–239
event handlers, 237–239
finished app, 210
project setup, 210–212
RecyclerView
adapter, 215–219
layouts, 213–214
MainActivity, 220–221
model, 215
overview of, 212–213
Room database
AppDatabase class, 223–225
Gradle dependencies for, 221–222, 225–226
LiveData class, 230–233
MainActivity, 226–227
TodoItem class, 222
TodoItemDao class, 222–223
ViewModel class, 227–230
L
lambda expressions, 5–6
as arguments to higher-order functions, 42
assigning, 40
defining, 40
definition of, 37
implicit arguments, 41
with receivers, 60–61
suspending, 164
type inference, 40–41
lateinit keyword, 73
lateinit property, 136, 139
late-initialized properties, 72–73
LaTeX, 316
launch coroutine builder, 164–169
layouts
Anko
Anko dependencies, 329–330
custom views, 334–335
layout parameters, 330–331
migrating Kudoo’s AddTodoActivity to, 332
modularizing, 333
previewing, 331
simple layout example, 330
XML layouts versus, 335
creating programmatically, 328–329
Nutrilicious app
DetailsActivity, 293–296
Favorites fragment, 276
item layout, 244–246
MainActivity, 243–244
SwipeRefreshLayout, 271–272
overview of, 328
RecyclerView, 213–214
SearchFragment, 268–269
XML, 335
lazy evaluation
concept of, 62
definition of, 38
lazy sequences
creating, 63–64
drop function, 64–65
overview of, 63–66
performance of, 65–66
take function, 64–65
lazy properties, 75–76
lazy sequences
creating, 63–64
drop function, 64–65
overview of, 63–66
performance of, 65–66
take function, 64–65
leading migration to Kotlin
buy-in, gaining, 347–348
knowledge sharing, 348–349
Lee, Christina, 347
let function, 53–54
libraries. See also interoperability with Java
Anko. See Anko library
AutoValue, 348
Buck, 7
Butter Knife, 348
Gradle, 7
Gson, 346
Lombok, 348
Mockito, 7
Moshi, 346
Retrofit, 7
Retrolambda, 348
linkedMapOf function, 46
linkedSetOf function, 46
List type, 127
listOf function, 46
LiveData class, 230–233
locks, 154
Lombok, 348
Long data type, 13
long[] type, 127
LongArray! type, 127
longArrayOf method, 128
loops
do-while, 19
for, 20–21
suspending functions, 161
while, 19
lparams function, 330–331
Lyft, 8
M
main function, 22–23
MainActivity
Kudoo app, 220–221, 226–227
Nutrilicious app
RecyclerView, 243–244, 248–250
SearchFragment, 269
makeSection function, 309
map function, 48
Map type, 127
mapOf function, 46
mapped types, 125
mapping data
collections, 48
data types, 14
Nutrilicious app
DTOs to models, 260–262
JSON to DTOs, 258–260
overview of, 257–258
visibilities
exception handling, 145–146
inline functions, 145
KClass, 143–144
Nothing type, 148
overview of, 143
signature clashes, 144–145
variant types, 146–148
maps, properties delegated to, 78–79
maxBy function, 49–50
maximum, calculating, 49–50
methods. See also functions
calling, 80–81
constructors
primary, 70, 82–83
secondary, 84
declaring, 80–81
doubleArrayOf, 128
extension, 81
fromString, 309
get, 71
getFoods, 257
getItemCount, 216, 218–219, 246
getters, 71, 124–125
intArrayOf, 128
iterator, 124
Java
calling as operators, 131
vararg methods, calling, 130–131
longArrayOf, 128
observeFavorites, 287
onAttach, 269
onBindViewHolder, 216, 218, 246
onCreate, 274
onCreateOptionsMenu, 267
onCreateView, 269
onCreateViewHolder, 216, 218, 246
onNewIntent, 267
onViewCreated, 269
overloading, 141
overriding, 80–81
recoverOrBuildSearchFragment, 274
set, 71
setItems, 232–233, 248
setters, 71, 124–125
setUpRecyclerView, 287
setUpSearchRecyclerView, 270
snackbar, 312
static, calling from Java, 140
toString, 309
migration
to Anko layouts, 332
to Gradle Kotlin DSL, 335–336
benefits of, 342
buildSrc directory, 340–342
drawbacks of, 342–343
Gradle settings, 336
module build script, 338–340
root build script, 336–338
to Kotlin, 7–8
autoconverted code, adjusting, 355–356
full migration, 350–351
Java-to-Kotlin converter, 355
leadership for, 346–349
partial migration, 349–350
pet projects, 353–354
planning, 354
production code, 352–353
risks and benefits of, 345–346
test code, 351–352
minBy function, 49–50
minimums, calculating, 49–50
minus sign (-)
dec operator (--), 28
minusAssign operator (-=), 28
subtraction operator (-), 28
MochK, 353
Mockito, 7, 353
models
mapping DTOs to, 260–262
RecyclerView, 215
modifiers. See keywords
modularizing Anko layouts, 333
module build script, migrating to Gradle Kotlin DSL
android block, 338–339
dependencies, 339–340
experimental features, 340
plugins block, 336–338
Moshi, 251, 346
Gradle dependencies for, 258
MoshiConverterFactory, 253
multiplication (*) operator, 28
multitasking
cooperative, 157–158
definition of, 152–153
preemptive, 157–158
multithreading, 152–153
mutable states, 153
mutable variables, 13
(Mutable)List type, 127
MutableList type, 127
mutableListOf function, 46
(Mutable)Map type, 127
mutableMapOf function, 46
mutableSetOf function, 46
mutual exclusion, 153
N
named parameters, 23–24
navigation, Nutrilicious app, 242–243, 301
NDBNO (nutrition database number), 260
nested classes, 82
nesting, 323–325
NetBeans, 7
Netflix, 8
NetworkDispatcher, 256
New Project command, 210
Newton, Joseph Fort, 315
nonblocking, 152
non-local returns, 44–45
not (!) operator, 15
Nothing type, 148
@NotNull, 126
nullability
annotations for, 128–129
mapped types and, 125
null safety, 29
nullable types
casting, 90
elvis operator (?:), 30–31
overview of, 29
safe call operator (?), 29–30
unsafe call operator (!!), 31, 355
overview of, 125
platform types, 126–128
@Nullable, 126
NullPointerException, 4, 8, 29
Nutrient class, 308
NutrientListConverter, 302–303
NutrientTypeConverter class, 303
Nutrilicious app
data mapping
DTOs to models, 260–262
JSON to DTOs, 258–260
overview of, 257–258
DetailsActivity
click handler, 297
data display, 300–301
DetailsViewModel class, 298–300
FOOD_ID_EXTRA identifier, 298
item click listener, 296–297
layout, 293–296
navigation, 301
string resources for headline, 296
empty search results, indicating, 311–312
Favorites fragment, 276–280
finished app, 241
food details, storing in database, 302–306
migrating to Gradle Kotlin DSL, 335–336
benefits of, 342
buildSrc directory, 340–342
drawbacks of, 342–343
Gradle settings, 336
module build script, 338–340
root build script, 336–338
progress indicator, 312–313
project setup, 242–243
RDIs (recommended daily intake), adding, 307–311
RecyclerView
adapter, 246–248
displaying mapped data in, 261–262
Food class, 246
item layout, 244–246
MainActivity layout, 243–244
MainActivity setup, 248–250
overview of, 242–243
Room database
AppDatabase class, 282
coroutine dispatchers, 283
FavoritesDao interface, 281
FavoritesViewModel class, 282–283, 286–287
Food class, 280–281
SearchFragment, 287–288
SearchListAdapter class, 283–284
toggleFavorite function, 285
search interface
implementation of, 265–268
overview of, 265
SearchFragment
adding to UI, 273
fragment transactions, 272
getViewModel extension, 270–271
layout for, 268–269
MainActivity, 269
methods, 269–270
onCreate method, 274
properties, 272
recoverOrBuildSearchFragment method, 274
restoring, 274
search intents, 275
setUpSearchRecyclerView function, 271
storing in activity’s state, 273
swipe refresh, 275
SwipeRefreshLayout, 271–272
updateListFor function, 271
SearchViewModel class, 262–264
USDA Food Reports API, fetching data from, 288–293
USDA Nutrition API, fetching data from
API requests, performing, 256–257
Gradle dependencies for, 250–251
Retrofit interface, 251–256
nutrition database number (NDBNO), 260
O
object expressions, 101–102
object keyword, 101–102, 130
object orientation. See also classes; objects
generics
benefits of, 105
classes, 106–107
functions, 107–108
reification, 108–109
type parameters, 105–106
inheritance
abstract classes, 87–88
closed-by-default principle, 85, 88
data classes, 96
interfaces, 85–86
open classes, 88–89
overriding rules for, 89
overview of, 84–85
overview of, 69
polymorphism
compile-time, 108
parametric, 108
type casting
ClassCastException, 90
nullable types, 90
smart casts, 90–91
type checking, 89
type parameters, 119–121
variance
bounded type parameters, 118–119
contravariance, 112
covariance, 110–112
declaration-site, 113–116
Java interoperability, 115, 116
star projections, 119–121
use-site, 116–118
visibilities
class declarations, 92–93
interface declarations, 92–93
top-level declarations, 93–94
Object[] type, 127
objects
BuildPlugins, 342
companion, 103–105
creating, 101–102
declaring, 103
DefaultDispatcher, 165
DTOs (data transfer objects)
DetailsDto, 291
mapping JSON data to, 258–260
mapping to models, 260–262
wrappers for, 290
equality
checking, 32
floating point, 32–33
initializing, 55
instantiating, 69
Job, 177–181
object expressions, 101–102
usdaApi, 256–257
observable properties, 76–77
observeFavorites method, 287
OkHttp, 251, 253–255
onAttach method, 269
onBindViewHolder method, 216, 218, 246
onCreate method, 274
onCreateOptionsMenu method, 267
onCreateView method, 269
onCreateViewHolder method, 216, 218, 246
online resources, 7
onNewIntent method, 267
onViewCreated method, 269
OO. See object orientation
open classes, 88–89
open keyword, 87
operators
as, 90
elvis (?:), 30–31
indexed access, 47
is, 89
Java methods as, 131
safe call (?), 29–30
table of, 28
ternary conditional, 18
unsafe call (!!), 31, 355
or (||) operator, 15
Oracle, Google’s lawsuit with, 5
overloading
functions, 24–25
methods, 141
override keyword, 86, 104–105
overriding
inheritance and, 89
methods, 80–81
P
pair programming, 348
parallelism, 152–153
parameters, function, 23–24
parametric polymorphism, 108
partial migration, 349–350
percent sign (%)
rem operator (%), 28
remAssign operator (%=), 28
performance, DSLs (domain-specific languages), 327–328
period (.), 28
Perlis, Alan J.345
pet projects, integrating Kotlin into, 353–354
Pinterest, 8
pipe symbol (|) operator, 15
plans, migration, 354
platform types, 126–128
plugins block (Gradle Kotlin DSL), 336–338
plus function, 180
plus sign (+), 28
addition operator (+), 28
inc operator (++), 28
plusAssign operator (+=), 28
pointers to functions, 39
polymorphism, 80–81
compile-time, 108
parametric, 108
populating databases, 224–225
pragmatic programming, 4
preemptive multitasking, 157–158
previewing Anko layouts, 331
primary constructors, 70, 82–83
printAll function, 119–121
private keyword, 92, 94
produce coroutine builder, 191
producers, creating, 191
productFlavors block (Gradle Kotlin DSL), 339
production code, integrating Kotlin into, 352–353
progress indicator (Nutrilicious app), 312–313
projects
creating
Kudoo app, 210–212
Nutrilicious app, 242–243
integrating Kotlin into, 353–354
promises, 152
properties
adding to classes, 70–71
backing fields, 72
calling from Java, 133–135
delegated
implementation of, 74–75
lazy properties, 75–76
maps, 78–79
observable properties, 76–77
syntax of, 74
exposing as fields, 135–136
fields compared to, 71
late-initialized, 72–73
lazy, 75–76
observable, 76–77
SearchFragment, 272
protected keyword, 92
public keyword, 92, 94
public scope, 26
Python, 316
Q
@Query annotation, 255
question mark (?)
elvis operator (?:), 30–31
safe call operator (?), 29–30
R
race conditions, 154
ranges in for loops, 20–21
rangeTo operator (.), 28
read-eval-print loop (REPL), 11
read-only variables, 13
receive function, 187
ReceiveChannel, 188
receivers, lambda expressions with, 60–61
recoverOrBuildSearchFragment method, 274
RecyclerListAdapter, 215–219
RecyclerView
Kudoo app
adapter, 215–219
layouts, 213–214
MainActivity, 220–221
model, 215
overview of, 212–213
Nutrilicious app
adapter, 246–248
displaying mapped data in, 261–262
Food class, 246
item layout, 244–246
MainActivity layout, 243–244
MainActivity setup, 248–250
overview of, 242–243
Reddit Kotlin community, 7
reduce function, 52
reduceRight function, 52
referential equality operator (===), 32
Reformat Code command (Code menu), 258
Refresh All Gradle Projects command, 336
reification, 108–109
reified keyword, 109
rem (%) operator, 28
renderNutrient function, 301, 310
rendezvous channels, 190
REPL (read-eval-print-loop), 11
requests (API), performing, 256–257
resolving extensions, 25–26
REST, 151
restoring fragments, 274
@RestrictsSuspension annotation, 185
Retrofit, 7, 251–256
Retrolambda, 348
returns, non-local, 44–45
Room database
Kudoo app
AppDatabase class, 223–225
Gradle dependencies for, 221–222, 225–226
LiveData class, 230–233
MainActivity, 226–227
TodoItem class, 222
TodoItemDao class, 222–223
ViewModel class, 227–230
Nutrilicious app
AppDatabase class, 282
coroutine dispatchers, 283
FavoritesDao interface, 281
FavoritesViewModel class, 282–283, 286–287
Food class, 280–281
SearchFragment, 287–288
SearchListAdapter class, 283–284
storing food details in, 302–306
toggleFavorite function, 285
root build scripts, migrating to Gradle Kotlin DSL
allprojects block, 337
buildscript block, 336–337
delete task, 337–338
run function, 56–58
runblocking coroutine builder, 163–164
RxJava, 182
S
safe call operator (?), 29–30
safety, 4
SAM (single abstract method) conversions, 131–132
Sample Data Directory, 258
Scala, 4
scope of extension functions, 26
scoping functions
also, 58–59
apply, 54–55
let, 53–54
run, 56–58
use, 59
with, 55–56
Scrum, 354
sealed classes
calling from Java, 142–143
declaring, 98–100
sealed keyword, 98–100
Search API, 255–256, 258–260
search interface, Nutrilicious app
implementation of, 265–268
overview of, 265
searchable configuration, 266
SearchFragment
adding to UI, 273
database connection, 287–288
fragment transactions, 272
getViewModel extension, 270–271
layout for, 268–269
MainActivity, 269
methods, 269–270
onCreate method, 274
properties, 272
recoverOrBuildSearchFragment method, 274
restoring, 274
search intents, 275
setUpSearchRecyclerView function, 271
storing in activity’s state, 273
swipe refresh, 275
SwipeRefreshLayout, 271–272
updateListFor function, 271
SearchListAdapter class, 246–248, 283–284
SearchViewModel class, 262–264
secondary constructors, 84
send function, 187
SendChannel, 187–188
sendEmail function, 24
Sequences, 6
sequences, lazy, 63–66
creating, 63–64
drop function, 64–65
performance of, 65–66
take function, 64–65
@SerializedName annotation, 261
set method, 71
set operator, 28
setItems method, 232–233, 248
setLoading function, 313
setOf function, 46
setters
calling, 124–125
declaring, 71
settings (Gradle), migrating, 336
setUpRecyclerView method, 287
setUpSearchRecyclerView function, 271
setUpSearchRecyclerView method, 270
sharing knowledge, 348–349
Short data type, 13
short-circuiting, 15
Show Kotlin Bytecode command, 217–218
signatures, function, 21–22, 39, 144–145, 159
Simula, 69
single abstract method (SAM) types, 131–132
single-expression functions, 22
singletons, declaring, 103
singleTop launch mode, 266
Slack, 8
Slack channel, 7
Smalltalk, 69
smart casts, 30, 90–91
snackbar method, 312
sorted function, 50
sortedArray function, 50
sortedBy function, 50
sortedDescending function, 50
sortedMapOf function, 46
sortedSetOf function, 46
sortedWith function, 50
sorting collections, 50–51
SoundCloud, 354
Spek, 353
Split Vertically command, 258
Spring, 124
SQL, 316
Square, 348
star projections, 119–121
statements. See keywords
static fields, calling from Java, 138–139
static keyword, 104
static methods, calling from Java, 140
static resolving of extensions, 25–26
Strategy pattern, 39
streams, 6, 62. See also sequences, lazy
String data type, 13, 127
string interpolation, 20
String! type, 127
structural equality operator (==), 32
subtraction (-) operator, 28
sumBy function, 49–50
sums, calculating, 49–50
super keyword, 87
support and community, 7
suspend keyword, 159–163, 198–200
suspendCoroutine function, 183
suspending functions, 157, 159–163, 164, 198–200
suspending lambda, 164
suspension points, 160
Swing, 182
swipe refresh, 275
SwipeRefreshLayout, 271–272
T
take function, 64–65
ternary conditional operator, 18
test code, integrating Kotlin into, 351–352
testing frameworks, 353
this keyword, 61
threads, 152, 157–158. See also coroutines
throw keyword, 33
throwing exceptions, 33–35
@Throws annotation, 145–146, 149
TimeoutCancellationException, 177
timeouts, handling, 177
toast function, 330
TodoItem class, 222
TodoItemDao class, 222–223
toggleFavorite function, 285
top-level declarations, 93–94
top-level extensions, calling, 137
toSortedMap function, 50
toString method, 309
transactions, fragment, 272
Trello, 8
try keyword, 33–35
try-catch blocks, 33–35
Twain, Mark, 3
type converters (Nutrilicious app), 302–303
type inference, 14–15, 40–41
@TypeConverter annotation, 303
@TypeConverters annotation, 303
types. See also nullability
algebraic, 100
aliases, 107
Any, 88
casting
ClassCastException, 90
nullable types, 90
smart casts, 30, 90–91
checking, 89
classes versus, 110
function types, 39
generic type parameters, 105–106
mapped, 125
mapping, 14
Nothing, 148
nullable
casting, 90
elvis operator (?:), 30–31
overview of, 29
safe call operator (?), 29–30
unsafe call operator (!!), 31, 355
parameters, 40–41, 105–106
platform types, 126–128
table of, 13–14
type inference, 14–15
variance
bounded type parameters, 118–119
contravariance, 112
covariance, 110–112
declaration-site, 113–116
star projections, 119–121
use-site, 116–118
type-safe builders, 320. See also DSLs (domain-specific
languages)
U
Uber, 8
Udacity, 8, 354
unchecked exceptions, 35–36
unlimited channels, 189
unsafe call operator (!!), 31, 355
updateListFor function, 271, 275, 311
updateUiWith function, 313
USDA Food Reports API, fetching data from, 288–293
USDA Nutrition API, fetching data from
API requests, performing, 256–257
Gradle dependencies for, 250–251
Retrofit interface, 251–256
usdaApi object, 256–257
use function, 59
User class
adding addresses to
multiple addresses, 323–325
single addresses, 319–320
declaring, 319
user function, 318
user groups, 348
user objects, building with DSLs
complex objects, 318–320
simple objects, 318
UserBuilder class, 321
@UserDsl annotation, 325
use-site variance, 116–118, 146–148
V
val keyword, 12, 71, 83, 130
validation, 58
values
floating point, 32–33
for function parameters, 23–24
var keyword, 12–13, 71, 83, 130
vararg methods, calling, 130–131
variable-argument methods, calling, 130–131
variables
declaring, 12–13
in DSLs (domain-specific languages), 326
immutable, 13
mutable, 13
read-only, 13
variance
bounded type parameters, 118–119
contravariance, 112
covariance, 110–112
declaration-site, 113–116
Java interoperability, 115, 116
star projections, 119–121
use-site, 116–118, 146–148
variant types, 146–148
verticalLayout function, 330
ViewHolder class, 216–217
ViewModel class
Kudoo app, 227–230
Nutrilicious app, 262–264
views
Anko layouts, 334–335
Kudoo RecyclerView
adapter, 215–219
layouts, 213–214
MainActivity, 220–221
model, 215
overview of, 212–213
Nutrilicious RecyclerView
adapter, 246–248
displaying mapped data in, 261–262
Food class, 246
item layout, 244–246
MainActivity layout, 243–244
MainActivity setup, 248–250
overview of, 242–243
visibilities
class declarations, 92–93
interface declarations, 92–93
mapping to Java
exception handling, 145–146
inline functions, 145
KClass, 143–144
Nothing type, 148
overview of, 143
signature clashes, 144–145
variant types, 146–148
top-level declarations, 93–94
W
WeightUnit enum, 309
Wharton, Jake, 348
when keyword, 15–19, 99–100
where keyword, 118–119
while loops, 19
Wigmore, Ann, 241
wildcard generation, 146–148
with function, 55–56
withContext function, 176
withTimeout function, 176
WordPress, 8
wrappers
for asynchronous APIs, 195–197
for DTOs (data transfer objects), 290
X-Y-Z
XML layouts, 335
yield function, 184
Ziglar, Zig, 11
Credits
Cover: Kronstadt at Sunrise, A.F. Smith/Shutterstock.