tl;dr property syntax is great but you should avoid properties hiding expensive operations in their custom getter/setter as it can be misleading for those using the API from outside. Prefer functions instead in those cases.
Unlike Java, Kotlin has first-class properties. In Kotlin, a property can be read-only — or not, can have delegates (such as Lazy
), and it can be a simple field-backed property or have custom getters and setters.
In this article we’re going to be focusing on these custom getters, and we’ll try to understand when it’s ok to use them, and when instead it might be counter-productive and obfuscating the meaning. If you want to read more on custom getters and setters, including some examples of good use, you can read the third article in this series.
Look at this interface:
interface DatabaseProvider {
val database: SQLiteDatabase
}
It’s a contrived example, but highlights a problem: can you tell what exactly is the property giving you? Is it a shared SQLiteDatabase
instance, or is it a fresh new instance every time? There’s no way to know without going to check the code, if it’s even available to whoever is using it.
It’s important to know, because if it’s a shared instance you should never close it, or you’ll break all other users of that database instance. If it’s not a shared instance, then every access to that property will have a significative cost.
Now look at this interface:
interface DatabaseProvider {
fun openDatabase(): SQLiteDatabase
}
Where with the property-based interface you couldn’t tell what was going on under the hood, now you can be sure that you’ll get a fresh new instance of the database every time you invoke the function. The function is also named in a non-ambiguous way, to further dissipate any doubts.
In this case, using a function instead of a property makes it crystal clear what to expect from it; using a property is more opaque and can lead to subtle and hard-to-fix bugs if the users of that interface don’t make the correct assumptions.
As a rule of thumb, prefer functions to properties which do any non-trivial work. Custom getters are really good for some scenarios, but shouldn’t be an excuse for creating properties that are effectively functions without parameters.
Remember that while functions are semantically meant to perform work, most people tends to conflate properties to Java fields. Breaking this expectation can lead to some rather unpleasant problems. Consider using properties only when you would be using a field in Java — possibly with some light transformation. In all other cases prefer functions, especially if they involve anything with a cost: memory/GC-heavy objects, significative computation, etc.
Your code will have a clear API interface that will not hold any dangerous surprise for its users.
Want to read more?
Take a look at the other articles in this series.