The syntax and rules of extension functions are explained also explained elsewhere, but this page is more tutorial style, covers the background and ‘why do we need extensions’ and ‘what is the benefit’, together with simple examples, and even useful examples.
The topics are:
- What are extension functions/properties?
- What is the advantage of extension functions?
- A simple example
- Smoke, Mirrors and Limitations
- Simple real world usage
- The reference syntax and details
What are extension functions/properties?
The kotlin language allows for extending existing classes, adding new methods and properties to already declared classes.
Extensions are adding new things to a class, from outside the class. The class may exist in a library or another module and there may not be access to modify the class, and extensions allow extending the class even in this situation.
Traditionally, to add new methods to a class, you create a derived class which inherits all from the existing class and add your extension methods/properties to the new derived class. The result is a class hierarchy.
Extension functions/properties allow you to extend classes already defined and even extend in your code classes defined in external libraries. The result is the original class is extended in place of creating a new derived class in addition to the original class.
What is the advantage of extension functions?
You can use the derived class in place of the base class, but the limitation is you cannot use the base class in place of the derived class. This means passing the derived class to existing code is no problem, but the returned values from existing code all being the base class is a limitation. Base class objects must be used to instance new derived class objects, but at the expense of an overhead more bloated source code that is less readable. The real world example will illustrate this point.
Code can create a new class to extend Integer for example, and values of the new class will work with all the standard Integer functions, but these standard functions all return Integers, not the new extension class, so the result of every operation needs a messy type conversion.
A Simple example.
The following code provides a simple example (rather useless) base class.
class TestEnv(){ var envVar = 3 fun testInEnv(func: TestEnv.()->Unit){ func() } fun envDoubler(value: Int) = value * 2 } fun main(args: Array){ val test = TestEnv() val newNum = 4 test.testInEnv ({ envVar = envDoubler(newNum) }) }
Code that uses this base class, but cannot change the base class, could create a subclass with the new method.
class TestEnv2(): TestEnv(){ fun doubleEnvVar(){ envVar = value * 2 } }
But this has limitations that come from having a new class as discussed in advantages of extensions functions/properties.
So here is the extension method to the original class.
fun TestEnv.doubleEnvVar(){ envVar = value * 2 }
To create an extension function, simply declare the function as a normal function, but with the class name and ‘.’ character before the function name. All properties and methods within the class can be accessed seemingly just as if the method was defined in the original class.
Extension properties are created the same way, but using val
or var
keywords in place of fun.
If you think about it, you could write the same ‘extension function’ as conventional function that takes an instance of the class as a parameter.
fun doubleEnvVar(it: TestEnv){ it.envVar = value * 2 }
There is very little difference in the declaration between extension and regular function, beyond the syntax to call the extension function. The benefit is in the clearer calling syntax that allows more readable code, and allows for auto code completion simplifying discovering available options when coding.
The reality is that the extension method can be seen as syntactic sugar for the function equivalent, and has the same limitations as that function equivalent. This means only public members of the class are accessible to the extension function. While protected class attributes would be available for a subclass, even protected attributes of the class cannot be accessed by the code of extension classes/properties
Simple real world Example.
The standard numeric types most languages are integer and float. Float is fine for many applications, but if you use float for currencies, you can get errors due to the fact representation of some numbers are inexact, and the solution is to use the Decimal Type.
The problems with Decimals types there is literally no standard way to have good literals.
>>> (5.8 - 5.6)*10 == 2 False #beware of intermediate errors >>> from decimal import Decimal >>> (Decimal(5.8) - Decimal(5.6))*10 == Decimal(2) False >>> (Decimal("5.8") - Decimal("5.6"))*10 == Decimal(2) True
Now, extension function and properties to the rescue. We can create an extension function toBd() to more neatly convert literals. In fact we could consider the BigDecimal value of an Int as a property of that Int, and define and extension property creating even less boilerplate clutter.
import java.math.BigDecimal fun Int.toBd(): BigDecimal{ return BigDecimal(this) } val String.bd get() = BigDecimal(this) val Int.bd get() = BigDecimal(this) val Double.bd get() = BigDecimal.valueOf(this) val num1 = 3.toBd() val num2 = 3.bd val num3 = "3.14".bd val num4 = 3.14.bd // equivalent to python (5.8.bd - 5.6bd) * 10 == 2.bd
The final line of the kotlin example above is far more concise than the equivalent python example, and a is a brief simple example with a genuine benefit.
[…] of the python None). “func: TestEnv.()->Unit’ is a lambda that will be an extension function for an instance of the TestEnv […]
LikeLike
[…] Extension functions, and Lambdas and even Extension Lambdas have both been discussed already (and are assumed knowledge for this page). But the uses of lambdas goes beyond just functionality, and into the realm of expressive code. Expressive code is about having the code represent the ideas behind the code more clearly, allowing better reading of the code and even more importantly, further exploring of the ideas behind to code as a basis for enhancing the technology delivered by the code. […]
LikeLike