#31DaysOfKotlin — Week 2 Recap

Sean McQuillan
Android Developers
Published in
5 min readApr 12, 2018

--

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.

In the second we we continued to explore Kotlin — going deeper into topics like sealed classes and inline.

Check out the other recaps:

Day 8: Visibility

In Kotlin, everything is public by default! Well, almost. Kotlin has a rich set of visibility modifiers you can use as well: private, protected, internal. Each of them reduces the visibility in a different way. Docs: visibility modifiers

// public by default
val isVisible = true
// only in the same file
private val isHidden = true
// internal to compilation ‘module’
internal val almostVisible = true
class Foo {
// public by default
val isVisible = true
// visible to my subclasses
protected val isInheritable = true
// only in the same class
private val isHidden = true
}

Day 9: Default arguments

Is the number of method overloads getting out of hand? Specify default parameter values in functions. Make the code even more readable with named parameters. Docs: default arguments

// parameters with default values
class BulletPointSpan(
private val bulletRadius: Float = DEFAULT_BULLET_RADIUS,
private val gapWidth: Int = DEFAULT_GAP_WIDTH,
private val color: Int = Color.BLACK
) {…}
// using only default values
val bulletPointSpan = BulletPointSpan()
// passing a value for the first argument, others default
val bulletPointSpan2 = BulletPointSpan(
resources.getDimension(R.dimen.radius))
// using a named parameter for the last argument, others default
val bulletPointSpan3 = BulletPointSpan(color = Color.RED)

Day 10: Sealed classes

Kotlin sealed classes let you easily handle error data. When combined with LiveData you can use one LiveData to represent both the success path and the error path. Way better than using two variables. Docs: sealed classes

sealed class NetworkResult
data class Success(val result: String): NetworkResult()
data class Failure(val error: Error): NetworkResult()
// one observer for success and failure
viewModel.data.observe(this, Observer<NetworkResult> { data ->
data ?: return@Observer // skip nulls
when(data) {
is Success -> showResult(data.result) // smart cast to Success
is Failure -> showError(data.error) // smart cast to Failure
}
})

You can also use sealed classes in a RecyclerView adapter. They’re a perfect fit for ViewHolders— with a clean set of types to dispatch explicitly to each holder. Used as an expression, the compiler will error if all types aren’t matched.

// use Sealed classes as ViewHolders in a RecyclerViewAdapter
override fun onBindViewHolder(
holder: SealedAdapterViewHolder?, position: Int) {
when (holder) { // compiler enforces handling all types
is HeaderHolder -> {
holder.displayHeader(items[position]) // smart cast here
}
is DetailsHolder -> {
holder.displayDetails(items[position]) // smart cast here
}
}
}

Going further with RecyclerViews, if we have a lot of callbacks from a RecyclerView item, like this one with detail clicks, shares, and delete actions, we can use sealed classes. One callback taking one sealed class can handle all the things!

sealed class DetailItemClickEvent
data class DetailBodyClick(val section: Int): DetailItemClickEvent()
data class ShareClick(val platform: String): DetailItemClickEvent()
data class DeleteClick(val confirmed: Boolean):
DetailItemClickEvent()
class MyHandler : DetailItemClickInterface {
override fun onDetailClicked(item: DetailItemClickEvent) {
when (item) { // compiler enforces handling all types
is DetailBodyClick -> expandBody(item.section)
is ShareClick -> shareOn(item.platform)
is DeleteClick -> {
if (item.confirmed) doDelete() else confirmDetele()
}
}
}
}

Day 11: Lazy

It’s good to be lazy! Defer the cost of expensive property initialization until they’re actually needed, by using lazy. The computed value is then saved and used for any future calls. Docs: lazy

val preference: String by lazy {
sharedPreferences.getString(PREFERENCE_KEY)
}

Day 12: Lateinit

In Android, onCreate or other callbacks initialize objects. In Kotlin non-null vals must be initialized. What to do? Enter lateinit. It’s a promise: initialize me later! Use it to pinky-swear it will eventually be null safe. Docs: lateinit

class MyActivity : AppCompatActivity() {
// non-null, but not initalized
lateinit var recyclerView: RecyclerView

override fun onCreate(savedInstanceState: Bundle?) {
// …
// initialized here
recyclerView = findViewById(R.id.recycler_view)
}
}

Day 13: Require and check

Are your function arguments valid? Check before using them, with require. If they’re not valid an IllegalArgumentException is thrown. Docs: require

fun setName(name: String) {
// calling setName(“”) throws IllegalArgumentException
require(name.isNotEmpty()) { “Invalid name” }

// …
}

Is the state of your enclosing class correct? Use check to verify. It will throw an IllegalStateException if the value checked is false. Docs: check

fun User.logOut(){
// When not authenticated, throws IllegalStateException
check(isAuthenticated()) { “User $email is not authenticated” }
isAuthenticated = false
}

Day 14: Inline

Can’t wait to use lambdas to make new APIs? Get in line. Kotlin lets you specify a function as inline — which means calls will be replaced with the function body. Breathe and make lambda-based APIs with zero overhead. Docs: inline functions

// define an inline function that takes a function argumentinline fun onlyIf(check: Boolean, operation: () -> Unit) {
if (check) {
operation()
}
}
// call it like this
onlyIf(shouldPrint) { // call: pass operation as a lambda
println(“Hello, Kotlin”)
}
// which will be inlined to this
if (shouldPrint) { // execution: no need to create lambda
println(“Hello, Kotlin”)
}

This week went deeper into Kotlin features: visibility, default arguments, sealed classes, lazy, lateinit, require and check, and the really powerful inline. Next week we’ll dive into more Kotlin features and start exploring Android KTX.

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

--

--