Programming Kotlin Applications: Building Mobile and Server-Side Applications with Kotlin drops readers into the fast lane for learning to develop with the Kotlin programming language. Authored by accomplished cloud consultant and technology professional Brett McLaughlin, Programming Kotlin Applications provides readers with the pragmatic and practical advice they need to build their very first Kotlin applications.
Designed to give readers a thorough understanding of Kotlin that goes beyond mere mobile programming, this book will help you:
- Learn how to develop your first Kotlin project
- Understand how Kotlin securely protects and stores information
- Advocate for using Kotlin in your own professional and personal environments
- Understand Kotlin's goals and how to use it as its best
- Know when to avoid using Kotlin
Table of Contents
Introduction xxv
Chapter 1: Objects All The Way Down 1
Kotlin: A New Programming Language 1
What is Kotlin? 2
What Does Kotlin Add to Java? 3
Kotlin is Object-Oriented 3
Interlude: Set Up Your Kotlin Environment 4
Install Kotlin (and an IDE) 4
Install IntelliJ 5
Create Your Kotlin Program 8
Compile and Run Your Kotlin Program 9
Fix Any Errors as They Appear 10
Install Kotlin (and Use the Command Line) 10
Command-Line Kotlin on Windows 10
Command-Line Kotlin on Mac OS X 11
Command-Line Kotlin on UNIX-Based Systems 12
Verify Your Command-Line Installation 12
Creating Useful Objects 13
Pass In Values to an Object Using Its Constructor 13
Print an Object with toString() 14
Terminology Update: Functions and Methods 15
Print an Object (and Do It with Shorthand) 15
Override the toString() Method 16
All Data is Not a Property Value 17
Initialize an Object and Change a Variable 19
Initialize a Class with a Block 19
Kotlin Auto-Generates Getters and Setters 20
Terminology Update: Getters, Setters, Mutators, Accessors 20
Constants Can’t Change (Sort of) 21
Chapter 2: It’s Hard To Break Kotlin 25
Upgrade Your Kotlin Class Game 25
Name a File According to Its Class 26
Organize Your Classes with Packages 27
Put Person in a Package 28
Classes: The Ultimate Type in Kotlin 31
Kotlin Has a Large Number of Types 31
Numbers in Kotlin 31
Letters and Things 32
Truth or Fiction 33
Types Aren’t Interchangeable (Part 1) 33
You Must Initialize Your Properties 34
Types Aren’t Interchangeable (Part 2) 35
You Can Explicitly Tell Kotlin What Type to Use 36
Try to Anticipate How Types Will Be Used 37
It’s Easy to Break Kotlin (Sort of) 37
Overriding Property Accessors and Mutators 37
Custom-Set Properties Can’t Be in a Primary Constructor 38
Move Properties Out of Your Primary Constructors 38
Initialize Properties Immediately 39
Try to Avoid Overusing Names 41
Override Mutators for Certain Properties 41
Classes Can Have Custom Behavior 43
Define a Custom Method on Your Class 43
Every Property Must Be Initialized 44
Assign an Uninitialized Property a Dummy Value 45
Tell Kotlin You’ll Initialize a Property Later 45
Assign Your Property the Return Value from a Function 46
Sometimes You Don’t Need a Property! 47
TYPE SAFETY CHANGES EVERYTHING 49
Writing Code is Rarely Linear 49
Chapter 3: Kotlin is Extremely Classy 51
Objects, Classes, and Kotlin 51
All Classes Need an equals(x) Method 52
Equals(x) is Used to Compare Two Objects 52
Override equals(x) to Make It Meaningful 54
Every Object is a Particular Type 56
A Brief Introduction to Null 58
Every Object Instance Needs a Unique hashCode() 59
All Classes Inherit from Any 59
Always Override hashCode() and equals(x) 61
Default Hash Codes Are Based on Memory Location 63
Use Hash Codes to Come Up with Hash Codes 63
Searching (and Other Things) Depend on Useful and Fast equals(x) and hashCode() 64
Multiple Properties to Differentiate Them in hashCode() 65
Use == over equals(x) for Speed 66
A Quick Speed Check on hashCode() 66
Basic Class Methods Are Really Important 67
Chapter 4: Inheritance Matters 69
Good Classes Are Not Always Complex Classes 69
Keep It Simple, Stupid 70
Keep It Flexible, Stupid 71
Classes Can Define Default Values for Properties 73
Constructors Can Accept Default Values 74
Kotlin Expects Arguments in Order 74
Specify Arguments by Name 74
Change the Order of Arguments (If You Need) 75
Secondary Constructors Provide Additional Construction Options 76
Secondary Constructors Come Second 76
Secondary Constructors Can Assign Property Values 77
You Can Assign null to a Property . . . Sometimes 79
null Properties Can Cause Problems 81
Handle Dependent Values with Custom Mutators 82
Set Dependent Values in a Custom Mutator 82
All Property Assignments Use the Property’s Mutator 83
Nullable Values Can Be Set to null! 84
Limit Access to Dependent Values 86
When Possible, Calculate Dependent Values 87
You Can Avoid Parentheses with a Read-Only Property 88
Need Specifics? Consider a Subclass 91
Any is the Base Class for Everything in Kotlin 91
{ . . . } Is Shorthand for Collapsed Code 93
A Class Must Be Open for Subclassing 94
Terminology: Subclass, Inherit, Base Class, and More 95
A Subclass Must Follow Its Superclass’s Rules 96
A Subclass Gets Behavior from All of Its Superclasses 96
Your Subclass Should Be Different Than Your Superclass 97
Subclass Constructors Often Add Arguments 97
Don’t Make Mutable What Isn’t Mutable 98
Sometimes Objects Don’t Exactly Map to the Real World 99
Generally, Objects Should Map to the Real World 99
Chapter 5: Lists and Sets and Maps, Oh My! 101
Lists Are Just a Collection of Things 101
Kotlin Lists: One Type of Collection 101
Collection is a Factory for Collection Objects 102
Collection is Automatically Available to Your Code 104
Mutating a Mutable List 105
Getting Properties from a Mutable List 105
Lists (and Collections) Can Be Typed 106
Give Your Lists Types 107
Iterate over Your Lists 108
Kotlin Tries to Figure Out What You Mean 111
Lists Are Ordered and Can Repeat 111
Order Gives You Ordered Access 112
Lists Can Contain Duplicate Items 112
Sets: Unordered but Unique 113
In Sets, Ordering is Not Guaranteed 114
When Does Order Matter? 115
Sort Lists (and Sets) on the Fly 115
Sets: No Duplicates, No Matter What 116
Sets “Swallow Up” Duplicates 116
Sets Use equals(x) to Determine Existing Membership 116
Using a Set? Check equals(x) 119
Iterators Aren’t (Always) Mutable 119
Maps: When a Single Value Isn’t Enough 119
Maps Are Created by Factories 120
Use Keys to Find Values 120
How Do You Want Your Value? 121
Filter a Collection by . . . Anything 121
Filter Based on a Certain Criterion 122
Filter Has a Number of Useful Variations 123
Collections: For Primitive and Custom Types 123
Add a Collection to Person 124
Allow Collections to Be Added to Collection Properties 126
Sets and MutableSets Aren’t the Same 127
Collection Properties Are Just Collections 128
Chapter 6: The Future (In Kotlin) is Generic 129
Generics Allow Deferring of a Type 129
Collections Are Generic 129
Parameterized Types Are Available Throughout a Class 130
Generic: What Exactly Does It Refer To? 131
Generics Try to Infer a Type When Possible 132
Kotlin Looks for Matching Types 132
Kotlin Looks for the Narrowest Type 132
Sometimes Type Inference is Wrong 133
Don’t Assume You Know Object Intent 133
Kotlin Doesn’t Tell You the Generic Type 134
Just Tell Kotlin What You Want! 134
Covariance: A Study in Types and Assignment 134
What about Generic Types? 135
Some Languages Take Extra Work to Be Covariant 137
Kotlin Actually Takes Extra Work to Be Covariant, Too 137
Sometimes You Have to Make Explicit What is Obvious 137
Covariant Types Limit the Input Type as Well as the Output Type 137
Covariance is Really about Making Inheritance Work the Way You Expect 138
Contravariance: Building Consumers from Generic Types 138
Contravariance: Limiting What Comes Out Rather Than What Comes In 139
Contravariance Works from a Base Class Down to a Subclass 141
Contravariant Classes Can’t Return a Generic Type 141
Does Any of This Really Matter? 142
Unsafevariance: Learning The Rules, then Breaking Them 142
Typeprojection Lets You Deal with Base Classes 143
Variance Can Affect Functions, Not Just Classes 143
Type Projection Tells Kotlin to Allow Subclasses as Input for a Base Class 144
Producers Can’t Consume and Consumers Can’t Produce 145
Variance Can’t Solve Every Problem 145
Chapter 7: Flying Through Control Structures 147
Control Structures Are the Bread and Butter of Programming 147
If and Else: The Great Decision Point 148
!! Ensures Non-Nullable Values 148
Control Structures Affect the Flow of Your Code 149
if and else Follow a Basic Structure 150
Expressions and if Statements 151
Use the Results of an if Statement Directly 152
Kotlin Has No Ternary Operator 153
A Block Evaluates to the Last Statement in That Block 153
if Statements That Are Assigned Must Have else Blocks 154
When is Kotlin’s Version of Switch 154
Each Comparison or Condition is a Code Block 155
Handle Everything Else with an else Block 156
Each Branch Can Support a Range 157
Each Branch Usually Has a Partial Expression 158
Branch Conditions Are Checked Sequentially 159
Branch Conditions Are Just Expressions 159
When Can Be Evaluated as a Statement, Too 160
For is for Looping 161
For in Kotlin Requires an Iterator 162
You Do Less, Kotlin Does More 163
For Has Requirements for Iteration 163
You Can Grab Indices Instead of Objects with for 164
Use While to Execute until a Condition is False 167
While is All about a Boolean Condition 167
A Wrinkle in while: Multiple Operators, One Variable 168
Combine Control Structures for More Interesting Solutions 169
Do . . . While Always Runs Once 170
Every do . . . while Loop Can Be Written as a while Loop 170
If Something Must Happen, Use do . . . while 171
do . . . while Can Be a Performance Consideration 175
Get Out of a Loop Immediately with Break 176
Break Skips What’s Left in a Loop 176
You Can Use a Label with break 177
Go to the Next Iteration Immediately with Continue 178
Continue Works with Labels as Well 179
If versus continue: Mostly Style over Substance 179
Return Returns 180
Chapter 8: Data Classes 183
Classes in the Real World Are Varied but Well Explored 183
Many Classes Share Common Characteristics 183
Common Characteristics Result in Common Usage 185
A Data Class Takes the Work Out of a Class Focused on Data 185
Data Classes Handle the Basics of Data for You 185
The Basics of Data Includes hashCode() and equals(x) 186
Destructuring Data through Declarations 188
Grab the Property Values from a Class Instance 188
Destructuring Declarations Aren’t Particularly Clever 189
Kotlin is Using componentN() Methods to Make Declarations Work 190
You Can Add componentN() Methods to Any Class 191
If You Can Use a Data Class, You Should 192
You Can “Copy” an Object or Make a Copy Of an Object 192
Using = Doesn’t Actually Make a Copy 192
If You Want a Real Copy, Use copy() 193
Data Classes Require Several Things from You 194
Data Classes Require Parameters and val or var 194
Data Classes Cannot Be Abstract, Open, Sealed, or Inner 195
Data Classes Add Special Behavior to Generated Code 195
You Can Override Compiler-Generated Versions of Many Standard Methods 196
Supertype Class Functions Take Precedence 196
Data Classes Only Generate Code for Constructor Parameters 197
Only Constructor Parameters Are Used in equals() 199
Data Classes Are Best Left Alone 200
Chapter 9: Enums and Sealed, More Specialty Classes 203
Strings Are Terrible as Static Type Representations 203
Strings Are Terrible Type Representations 204
Capitalization Creates Comparison Problems 205
This Problem Occurs All the Time 206
String Constants Can Help . . . Some 206
Companion Objects Are Single Instance 207
Constants Must Be Singular 208
Companion Objects Are Singletons 209
Companion Objects Are Still Objects 210
You Can Use Companion Objects without Their Names 211
Using a Companion Object’s Name is Optional 211
Using a Companion Object’s Name is Stylistic 213
Companion Object Names Are Hard 214
You Can Skip the Companion Object Name Altogether 215
Enums Define Constants and Provide Type Safety 216
Enums Classes Provide Type-Safe Values 216
Enums Classes Are Still Classes 218
Enums Give You the Name and Position of Constants 219
Each Constant in an enum is an Object 219
Each Constant Can Override Class-Level Behavior 220
Sealed Classes Are Type-Safe Class Hierarchies 221
Enums and Class Hierarchies Work for Shared Behavior 222
Sealed Classes Address Fixed Options and Non-Shared Behavior 222
Sealed Classes Don’t Have Shared Behavior 223
Sealed Classes Have a Fixed Number of Subclasses 224
Subclasses of a Sealed Class Don’t Always Define Behavior 225
when Requires All Sealed Subclasses to Be Handled 225
when Expressions Must Be Exhaustive for Sealed Classes 226
else Clauses Usually Don’t Work for Sealed Classes 228
else Clauses Hide Unimplemented Subclass Behavior 229
Chapter 10: Functions and Functions and Functions 233
Revisiting the Syntax of a Function 233
Functions Follow a Basic Formula 233
Function Arguments Also Have a Pattern 235
Default Values in Constructors Are Inherited 237
Default Values in Functions Are Inherited 238
Default Values in Functions Cannot Be Overridden 239
Default Values Can Affect Calling Functions 239
Calling Functions Using Named Arguments is Flexible 241
Function Arguments Can’t Be Null Unless You Say So 241
Functions Follow Flexible Rules 243
Functions Actually Return Unit by Default 243
Functions Can Be Single Expressions 244
Single-Expression Functions Don’t Have Curly Braces 245
Single-Expression Functions Don’t Use the return Keyword 246
Single-Expression Functions Can Infer a Return Type 246
Type Widening Results in the Widest Type Being Returned 248
Functions Can Take Variable Numbers of Arguments 249
A vararg Argument Can Be Treated Like an Array 251
Functions in Kotlin have Scope 251
Local Functions Are Functions Inside Functions 252
Member Functions Are Defined in a Class 252
Extension Functions Extend Existing Behavior without Inheritance 253
Extend an Existing Closed Class Using Dot Notation 253
this Gives You Access to the Extension Class 255
Function Literals: Lambdas and Anonymous Functions 257
Anonymous Functions Don’t Have Names 257
You Can Assign a Function to a Variable 258
Executable Code Makes for an “Executable” Variable 259
Higher-Order Functions Accept Functions as Arguments 260
The Result of a Function is Not a Function 260
Function Notation Focuses on Input and Output 261
You Can Define a Function Inline 263
Lambda Expressions Are Functions with Less Syntax 264
You Can Omit Parameters Altogether 266
Lambda Expressions Use it for Single Parameters 266
It Makes Lambdas Work More Smoothly 267
Lambda Expressions Return the Last Execution Result 267
Trailing Functions as Arguments to Other Functions 268
Lots of Functions, Lots of Room for Problems 268
Chapter 11: Speaking Idiomatic Kotlin 271
Scope Functions Provide Context to Code 271
Use Let to Provide Immediate Access to an Instance 272
let Gives You it to Access an Instance 273
The Scoped Code Blocks Are Actually Lambdas 274
let and Other Scope functions Are Largely about Convenience 275
You Can Chain Scoped Function Calls 275
An Outer it “Hides” an Inner it 276
Chaining Scope Functions and Nesting Scope Functions Are Not the Same 277
Nesting Scope Functions Requires Care in Naming 277
Chaining Scope Functions is Simpler and Cleaner 278
Prefer Chaining over Nesting 279
Many Chained Functions Start with a Nested Function 280
You Can Scope Functions to Non-Null Results 280
Accepting null Values Isn’t a Great Idea 282
Scope Functions Give You Null Options 282
Scope Functions Work on Other Functions . . . In Very Particular Ways 284
With is a Scope Function for Processing an Instance 287
with Uses this as Its Object Reference 287
A this Reference is Always Available 288
with Returns the Result of the Lambda 289
Run is a Code Runner and Scope Function 289
Choosing a Scope Function is a Matter of Style and Preference 290
run Doesn’t Have to Operate on an Object Instance 291
Apply Has a Context Object but No Return Value 292
apply Operates Upon an Instance 292
apply Returns the Context Object, Not the Lambda Result 293
?: is Kotlin’s Elvis Operator 293
Also Gives You an Instance . . . but Operates on the Instance First 294
also is Just Another Scope Function 295
also Executes before Assignment 296
Scope Functions Summary 298
Chapter 12: Inheritance, One More Time, With Feeling 303
Abstract Classes Require a Later Implementation 303
Abstract Classes Cannot Be Instantiated 304
Abstract Classes Define a Contract with Subclasses 306
Abstract Classes Can Define Concrete Properties and Functions 308
Subclasses Fulfill the Contract Written by an Abstract Class 310
Subclasses Should Vary Behavior 310
The Contract Allows for Uniform Treatment of Subclasses 311
Interfaces Define Behavior but Have No Body 313
Interfaces and Abstract Classes Are Similar 315
Interfaces Cannot Maintain State 316
A Class’s State is the Values of Its Properties 317
An Interface Can Have Fixed Values 317
Interfaces Can Define Function Bodies 318
Interfaces Allow Multiple Forms of Implementation 319
A Class Can Implement Multiple Interfaces 320
Interface Property Names Can Get Confusing 321
Interfaces Can Decorate a Class 321
Delegation Offers Another Option for Extending Behavior 322
Abstract Classes Move from Generic to Specific 322
More Specificity Means More Inheritance 324
Delegating to a Property 326
Delegation Occurs at Instantiation 329
Inheritance Requires Forethought and Afterthought 330
Chapter 13: Kotlin: The Next Step 331
Programming Kotlin for Android 331
Kotlin for Android is Still Just Kotlin 331
Move from Concept to Example 333
Kotlin and Java Are Great Companions 333
Your IDE is a Key Component 333
Kotlin is Compiled to Bytecode for the Java Virtual Machine 335
Gradle Gives You Project Build Capabilities 335
When Kotlin Questions Still Exist 335
Use the Internet to Supplement Your Own Needs and Learning Style 336
Now What? 337
Index 339