This is a follow-up to my last post, Extension Functions Can Be Utility Functions. Here I’ll look at more pros and cons of extension functions.
The article brought out 3 extension function use-cases.
- Adding a little local expressiveness with private methods like
- Adding methods you wish were available on types that you don’t own -
- Keeping your own classes small by defining operations as extension functions rather than actual methods -
On the Kotlin Slack Channel, Daniel Wellman asked
“How do you decide when methods should be extension functions and when they should be members (functions) on classes/objects _that you own_?”
Nathan Armstrong replied
“I think it’s usually when it encapsulates operations on a type in some context tangential to the one the type was made for. For example, when writing code that intersects two domains or concerns, using extension methods can help you use your existing types more naturally alongside one another without leaking that local utility into other parts of the codebase”
He’s right. So right that I’m going to say that again with many more words and claim the credit.
Most of my Java projects have ended up with a package of domain objects -
Address etc. Kotlin makes these nice and simple with data classes, and when 1.1 allows inheritance these will become even more useful.
Over time these classes accrete operations that make sense to our application. Things like
Address.isCloseMyHouse(). These operations dilute the essence of the class for our happy convenience, but after a while we can’t see the platonic wood for expedient trees.
Extension functions let us express the PersonNess in the
Person class, and the JSONness elsewhere. In this case maybe type-classes would be better, but for now, we can move our JSON writing and useful-to-us-but-not-really-essential operations off of the class and into extension functions.
A nice side-effect of this is that we can build our domain classes in a module that doesn’t have dependencies on Jackson and some SQL library and the whole of the rest of our app. Maybe we’ll finally get reuseable abstractions for those fundamental real-world things that every app has to implement from scratch because of all the special cases that define our Customer rather than yours.
A word of warning though. Extension functions are not polymorphic. If I have
class Person class Customer: Person fun Person.toJson() = ... fun Customer.toJson() = ...
the bindings are as follows:
Person().toJson() // calls Person.toJson() Customer().toJson() // calls Customer.toJson() val person: Person = readCustomer(42) person.toJson() // calls Person.toJson() no matter what the actual type of person
Thanks to Springer Nature for allowing me to publish examples from their code. If you’re looking for a Kotlin job in London, they are hiring - please contact me using one of the links in the sidebar.