kotlin-style-guide
kotlin-style-guide copied to clipboard
Using inline functions
Functions should only be made inline when they use inline-only features like inlined lambda parameters or reified types.
Inline functions should generally be short as they increase the code size on every invocation.
When an inline function that uses a reified type paremeter tends to become too long, consider extracting a part of the body into a separate, non-inline function. Example:
inline fun <reified T: Any> foo() = foo(T::class.java)
fun <T: Any> foo(clazz: Class<T>) {
//do something with clazz
}
When a class constructor expects a parameter of type Class<T> or KClass<T>, consider making an inline factory method so that the parameter can be omitted. Example:
class Foo <T : Any>(val clazz: Class<T>)
inline fun <reified T: Any> Foo() = Foo(T::class.java)
Functions should only be made inline when they use inline-only features like inlined lambda parameters or reified types.
I'd make a single exception for extensions meant to implement an operator or otherwise "rename" a member. It's unlikely to make a measurable performance impact, but there's probably cases where doing so is excusable.
// PointF is likely to be used in hot code paths for drawing
// and operator use will be compiled down to a field access
// rather than static method invocation
@Suppress("NOTHING_TO_INLINE")
inline operator fun PointF.component1() = x
@Suppress("NOTHING_TO_INLINE")
inline operator fun PointF.component2() = y
Also, factory functions using reified types only to access a Class should be as short as possible (ideally even a single expression). The bulk of the work that doesn't need to be inlined should be carried out in a separate function. This has the added benefit of exposing another API for runtime types.
fun complicatedFunction(clazz: Class<*>) {
// don't need a reified type for any work done here
// ...
}
inline fun <reified T : Any> complicatedFunction() = complicatedFunction(T::class.java)
Of course if you need the reified type for more than just a Class (is-checks, etc) then a longer body is fine.
One other thought that deserves its own issue (but I'll post here first, for the record):
What's the preference between using invoke on a companion object and a factory function?
class Foo <T: Any>(val clazz: Class<T>) {
companion object {
inline operator fun <reified T : Any> invoke() = Foo(T::class.java)
}
}
@damianw added #22 for the proposed factory function guidance
One more question on this:
What about inline to implement a member for an abstract (or open) type? For example, Android has LruCache<K, V> which allows override of create(key: K) to have the cache create values itself:
fun <K : Any, V : Any> lruCache(maxSize: Int, create: (K) -> V) = object : LruCache<K, V>(maxSize) {
override fun create(key: K): V = create(key)
}
This type of factory function in itself may be another issue, but should this function be inline? It appears to double the code size in simple cases, but maybe someone will squeeze performance out of it? Seems like creating a Function class and a subclass of LruCache would be similar.