As developers, we love shortcuts. They make our life easier and create less code. Less code tends to mean fewer chances for new bugs.

For instance, in Android, there is a shortcut for retrieving strings or colours in Context extending classes (like Activities). Instead of calling getResources().getString(resId) we can call getString(resId) directly.

Earlier this year, Google announced that Android Studio will support Kotlin as a first class language, giving us exciting new tools for solving problems.

With Kotlin, we can override operators and “extend” objects with new functions/methods or properties. Allowing us to write code like:

This would be instead of:

The second one is not just longer but it also has more elements. Raising the cognitive complexity of the code. There is not a lot of code here, but when looking at a large codebase, each increment in complexity makes a new coders (and original authors after 3 months) take longer time to wrap their heads around it.

How does this magic work?

It’s all a combination of extension functions and operators. The operator that we are using in this instance is get(Int). When overriding this function in an type we can access it by using the [..] operator as if it was an array or map:

This object call and returns the value provided by the mapRes provided function when requested. We can now create a variable in our context like:

As a side note, for anybody new to Kotlin, if the last argument of a function or constructor is a lambda, the parenthesis is optional and there is not new keyword.

This is ok, but if we have to write this on each of our classes, we aren’t really solving the problem entirely. This is where extension properties come in useful:

That’s a read-only property that gets “added” to any/all Context (and sub classes). The downside with this is that, each time dimens is accessed a new object is created. Therefore, it’s not recommended if we are doing time sensitive actions like drawing a view.

However, it’s always recommended to prepare variables before doing something inside the onDraw() method of a view.

Formatting a String

This is good for simple calls like getString(), or getDimension(). Some resources like formatted text requires some extra information. For this, we can have an intermediate object that exposes such functionality:

The invoke() operator represents (...) and allows the object behave like a function (or more if multiple are present). In this case we have 3 different ways to invoke the object depending of whether we require formatting, plurals or both:

And it compares with the traditional way as:

Not just for Context

If we want to have these extension properties on another place other than a Context (i.e. View or Fragment) we then have to copy and/or reference all the resources we have extended for all the base types.

However, we can create an interface to provide the context with an interface such as:

And add the property extensions to that interface:

Because both View and Fragment have the method getContext() we can then make them implement ContextAware and they will get all the properties:

But I don’t have a Context

If we are on a type that doesn’t have access to a Context, like a Thread or other third party, non Android type, we can still access the oneContext that is always available and cannot be leaked, the Application.

If we make the Application object extend ContextAware:

Then we can use it as a delegate:

The only thing to be aware of this is that the application Context doesn’t have a theme, so any values that are overridden in a theme will not be available and it’ll fallback to the default.

The rest of resource extensions code can be found here:

https://gist.github.com/pablisco/da25563d57559dd1d18f165272269b57

Feel free to make suggestions or comments here or on Twitter:

Software Artisan