The case for inline
functions is primarily to preserve performance, more specifically when one or more parameters are lambdas that would otherwise need to be wrapped in an object. As such, if a (non-reified) inline
function is defined without any lambda parameters, the following warning is given:
warning: expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types
Such functions also provide the same performance benefit when one or more parameters is of type kotlin.Number
, as seen below:
Non-inline
method: Number
is boxed.
private fun numToDouble(x: Number): Double {
return x.toDouble()
}
fun main() {
val double = numToDouble(2)
}
private final static numToDouble(Ljava/lang/Number;)D
L0
LINENUMBER 6 L0
ALOAD 0
INVOKEVIRTUAL java/lang/Number.doubleValue ()D
DRETURN
L1
LOCALVARIABLE x Ljava/lang/Number; L0 L1 0
MAXSTACK = 2
MAXLOCALS = 1
public final static main()V
L0
LINENUMBER 10 L0
ICONST_2
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; // Primitive is boxed as `Integer` (subtype of `Number`)
CHECKCAST java/lang/Number // Checked conversion to `Number`
INVOKESTATIC mathx/big/SrcKt.numToDouble (Ljava/lang/Number;)D
DSTORE 0
L1
LINENUMBER 11 L1
RETURN
L2
LOCALVARIABLE double D L1 L2 0
MAXSTACK = 2
MAXLOCALS = 2
inline
method: boxing is avoided
private inline fun numToDouble(x: Number): Double { // Warning is given here
return x.toDouble()
}
fun main() {
val double = numToDouble(2)
}
// Definition of generic `numToDouble`
// ...
public final static main()V
L0
LINENUMBER 10 L0
ICONST_2
ISTORE 2 // Primitive is used directly
// No type check needed
L1
ICONST_0
ISTORE 3
L2
LINENUMBER 12 L2
ILOAD 2
I2D
L3
LINENUMBER 10 L3
DSTORE 0
L4
LINENUMBER 11 L4
RETURN
L5
LOCALVARIABLE x$iv I L1 L3 2
LOCALVARIABLE $i$f$numToDouble I L2 L3 3
LOCALVARIABLE double D L4 L5 0
MAXSTACK = 2
MAXLOCALS = 4
In the example given, the use of inline
circumvents both primitive boxing and type checking. However, the compiler still complains that the performance gain is insignificant. The performance impact is even greater if the same function is inlined within code that is executed many times.
As a result, it is not logical that the warning should be given in this scenario. Maybe this can be changed in future versions of the language?
Sure, one may suppress the warning, but that is tedious when done repeatedly. One may also replace the Number
parameters with type T
given <reified T : Number>
. However, this is not semantically consistent with the usage of reified type parameters (that is, to obtain the class instance of a generic type at runtime).
EDIT: Stumbling upon similar discussions, reified
seems to be the accepted solution.