KOTLIN TIP #6

OBJECT EXPRESSIONS

Object expressions allow for strict singleton definition so there’s no mistaking it for a class that can be instantiated. They also ensure that you do not have to store singletons somewhere like in the Application class or as a static class variable.

For example, if I have a utility class with static thread-related methods I want to access throughout the app:

import android.os.Handler
import android.os.Looper
// notice that this is object instead of class
object ThreadUtil {
fun onMainThread(runnable: Runnable) {
val handler = Handler(Looper.getMainLooper())
handler.post(runnable)
}
}

 

ThreadUtil is called later in the typical way you would call a static class method:

ThreadUtil.onMainThread(runnable)

This means there’s no more declaring a constructor as private, or having to figure out where the static instance is stored. Objects are essentially first class citizens of the language. In a similar way, we create objects instead of anonymous inner classes:


viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float,
positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) {
bindUser(position)
}
})

Both of these do essentially the same thing — create a single instance of a class as a declared object.

KOTLIN TIP #1

LAZY LOADING

There are several benefits to lazy loading. Lazy loading can result in faster startup time, since loading is deferred to when the variable is accessed. This is particularly useful in using Kotlin for an Android app as opposed to a server app. For Android apps, we want to reduce app startup time so that the user sees the app content faster, rather than sitting at an initial loading screen.

Lazy loading like this is also more memory efficient, as we only load the resource into memory if it is called upon. Memory usage is important on mobile platforms like Android since phones have limited, shared resources. For example, if you are creating a shopping app, and there is a possibility that users will only browse your selection, you could have the actual purchasing API be lazy-loaded:

val productsApi: ProductsApi by lazy {
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(MoshiConverterFactory.create())
.build()
retrofit.create(ProductsApi::class.java)
}

By using lazy loading like this, if the user never attempts to check out in the app, you will never load the PurchasingApi, and therefore will not use up the resources it would take.

Lazy loading is also a good way to encapsulate initialization logic:

// bounds is created as soon as the first call to bounds is made
val bounds: RectF by lazy {
RectF(0f, 0f, width.toFloat(), height.toFloat())
}

As soon as the first reference to bounds is made, the RectF is created, using the view’s current width and height, saving us from having to explicitly create this RectF, then set it later on.