Skip to content
This repository has been archived by the owner on Dec 21, 2023. It is now read-only.

Using inline functions #16

Open
cypressious opened this issue Jun 1, 2016 · 5 comments
Open

Using inline functions #16

cypressious opened this issue Jun 1, 2016 · 5 comments

Comments

@cypressious
Copy link

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)
@damianw
Copy link

damianw commented Jun 8, 2016

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

@damianw
Copy link

damianw commented Jun 8, 2016

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.

@damianw
Copy link

damianw commented Jun 8, 2016

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)
  }
}

@yole
Copy link
Contributor

yole commented Jun 12, 2016

@damianw added #22 for the proposed factory function guidance

@damianw
Copy link

damianw commented Sep 20, 2016

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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants