#31DaysOfKotlin — Week 2 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.
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 = trueclass 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.