Eliminate Boilerplate using Kotlin Extensions
Kotlin’s extensions can be used to reduce boiler-plate in your code. To illustrate, let’s consider a simple custom View
. The custom view, MyView
, subclasses TextView. Let us suppose that the custom view has an associated styleable xml
The custom view can be incorporated into a layout xml using the attribute theText
:
The process of getting an attribute from an xml is generally done in the same way every time:
- The
TypedArray
is obtained fromContext.obtainStyledAttributes(…)
- Methods of the
TypedArray
are used to assign attribute’s values to class properties in atry
block - The
TypedArray
is recycled in afinally
block
Without simplification using Kotlin extensions, the implementation of MyView
is
Line 5 corresponds to Step 1; line 8 step 2; and line 10 step 3.
We can begin simplification by observing that only line 8 in the try/finally
block is truly unique to MyView
. The rest is boiler plate. We can relegate the try/finally
block to a Kotlin extension of TypedArray
:
We can simplify the MyView class as
The line which reads
text = getString(R.styleable.MyView_theText)
is the block
mentioned in lines 1 and 3 of the ViewCreateUtils
. The code which is unique to MyView
is passed to TypedArray.use(block: TypedArray.() -> Unit)
as a lambda function.
Further simplification is possible. Obtaining the TypedArray
from the Context
is done in the same way for custom views. We can define an extension on Context.
The final simplification of MyView
is
Remarkably, the TypedArray
is not directly mentioned in the simplified code. The extension function getStyledAttributes
does the work of obtaining the TypedArray
for us. The try/finally
block is not mentioned either. The use
extension function runs line 6 above for us in a try
block. Finally, the TypedArray
is recycled in a finally
block for us.
By using extension functions and lambdas, our initializer has gone from
init {
val typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.MyView, 0, 0)
try {
text = typedArray.getText(R.styleable.MyView_theText)
} finally {
typedArray.recycle()
}
to
init {
context.getStyledAttributes(attributeSet, R.styleable.MyView) {
text = getString(R.styleable.MyView_theText)
}
All of the boiler plate code has been eliminated. The simplified init
block only contains the essential code.