Illustration by Virginia Poltrack

#31DaysOfKotlin — Week 4 Recap

The more Kotlin code we write, the more we love it! Kotlin’s modern language features together with Android KTX made our Android code more concise, clear and pleasant. We (@objcode and @FMuntenescu) started the #31DaysOfKotlin series as a way of sharing some of our favorite Kotlin and Android KTX features and hopefully get more of you to like it as much as we do.

Week 4 looked at more language basics then dove into some ways Android KTX makes your code more concise and readable!

Check out the recaps for weeks 1, 2, and 3:

Day 22: Calling Kotlin from the Java Programming Language

Using Kotlin and Java in the same project? Do you have classes with top-level functions or properties? By default, the compiler generates the class name as YourFileKt. Change it by annotating the file with @file:JvmName. Docs: package level functions

// File: ShapesGenerator.kt
package com.shapes
fun generateSquare(): Square {…}
fun generateTriangle(): Triangle {…}
// Java usage:
Square square = ShapesGeneratorKt.generateSquare();
// File: ShapesGenerator.kt
@file:JvmName(“ShapesGenerator”)
package com.shapes
fun generateSquare(): Square {…}
fun generateTriangle(): Triangle {…}
// Java usage:
Square square = ShapesGenerator.generateSquare();

Day 23: Reified

To make the concept of reified concrete an example is in order: Context.systemService() in Android KTX uses reified to pass a “real” type via generics. No more passing classes to getSystemService! Docs: reified type parameters

Android KTX: Context.systemService()

// the old way
val alarmManager =
context.getSystemService(AlarmManager::class.java)
// the reified way
val alarmManager: AlarmManager = context.systemService()
// the magic from Android KTX… with “real” type T
inline fun <reified T> Context.systemService() =
getSystemService(T::class.java)

Day 24: Delegates

Delegate your work to another class with by. Favor composition over inheritance with class delegation and reuse property accessor logic with delegator properties. Docs: delegation and delegated properties

class MyAnimatingView : View( /* … */ ) {
// delegated property.
// Uses the getter and setter defined in InvalidateDelegate
var foregroundX by InvalidateDelegate(0f)
}
// A View Delegate which invalidates
// View.postInvalidateOnAnimation when set.
class InvalidateDelegate<T : Any>(var value: T) {
operator fun getValue(thisRef: View,
property: KProperty<*>) = value
    operator fun setValue(thisRef: View, 
property: KProperty<*>, value: T) {
this.value = value
thisRef.postInvalidateOnAnimation()
}
}

Day 25: Extension functions

No more Util classes! Extend the functionality of a class by using extension functions. Put the name of the class you’re extending before the name of the method you’re adding. Doc: extension functions

Extension functions:

  • Are not member functions
  • Do not modify the original class in any way
  • Are resolved an compile time by static type information
  • Are compiled to static functions
  • Don’t do polymorphism

Example: String.toUri()

// Extend String with toUri
inline fun String.toUri(): Uri = Uri.parse(this)
// And call it on any String!
val myUri = “www.developer.android.com".toUri()

Day 26: Drawable.toBitmap() easy conversions

If you ever converted a Drawable to a Bitmap then you know how much boilerplate you need.

Android KTX has a great set of functions to make your code more concise when working with classes from the graphics package. Docs: graphics

// get a drawable from resources
val myDrawable = ContextCompat.getDrawable(context, R.drawable.icon)
// convert the drawable to a bitmap
val bitmap = myDrawable.toBitmap()

Day 27: Sequences, lazy, and generators

Sequences are lists that never existed. A Sequence is a cousin of Iterator, lazily generating one value at a time. This really matters when using map and filter — they’ll create Sequences instead of copying the list for every step! Docs: sequences

val sequence = List(50) { it * 5 }.asSequence()
sequence.map { it * 2 }         // lazy (iterate 1 element)
.filter { it % 3 == 0 } // lazy (iterate 1 element)
.map { it + 1 } // lazy (iterate 1 element)
.toList() // eager (make a new list)

You can make sequences from a list or by specifying a next function. If you never terminate a sequence, it can be infinitely long without running out of memory. With coroutines in Kotlin you can also use generators! Docs: generators

// make a sequence from a list
list.asSequence().filter { it == “lazy” }
// or, reach infinity functionally
val natural = generateSequence(1) { it + 1 }
// or, cooperate with coroutines
val zeros = buildSequence {
while (true) {
yield (0)
}
}

Day 28: Easier spans

Powerful but hard to use — that’s how the text styling Spans API feels.

Android KTX adds extension functions for some of the most common spans and makes the API easier to use. Android KTX: spannable string builder

val string = buildSpannedString {
append(“no styling text”)
bold {
append(“bold”)
italic { append(“bold and italic”) }
}
inSpans(RelativeSizeSpan(2f), QuoteSpan()){
append(“double sized quote text”)
}
}

Day 29: Parcelize

Love the speed of Parcelable, but don’t like writing all that code? Say hello to @Parcelize. Spec: Parcelize

@Parcelize
data class User(val name: String, val occupation: Work): Parcelable
// build.gradle
androidExtensions {
//Enable experimental Kotlin features
// in gradle to enable Parcelize
experimental = true
}

Day 30: updatePadding extensions

Extending existing APIs with default arguments usually makes everyone happy. Android KTX lets you set the padding on one side of a view using default parameters. A one line function that saves so much code! Android KTX: View.updatePadding

view.updatePadding(left = newPadding)
view.updatePadding(top = newPadding)
view.updatePadding(right = newPadding)
view.updatePadding(bottom = newPadding)
view.updatePadding(top = newPadding, bottom = newPadding)

Day 31: Scoping out run, let, withapply

Let’s run with some standard Kotlin functions! Short and powerful, run, let, with, and apply all have a receiver (this), may have an argument (it) and may have a return value. See the differences

run

val string = “a”
val result = string.run {
// this = “a”
// it = not available
1 // Block return value
// result = 1
}

let

val string = “a”
val result = string.let {
// this = this@MyClass
// it = “a”
2 // Block return value
// result = 2
}

with

val string = “a”
val result = with(string) {
// this = “a”
// it = not available
3 // Block return value
// result = 3
}

apply

val string = “a”
val result = string.apply {
// this = “a”
// it = not available
4 // Block return value unused
// result = “a”
}

This week covered some more language features, such as interop, refied, and sequences, then we moved on to Android KTX showing off some of the ways it helps you write concise and readable code. To finish off the series we covered the powerful Kotlin scope functions.

Did you already start using Kotlin? We’d love to hear what other features you found great and how you used them in your Android app.

Thanks again to our reviewers: Jake, Romain, Nick, James, Don and our designer: Virginia.