dave08
01/04/2022, 12:20 PM@JvmInline
value class MethodParams1<A1>(val value: Array<Any?>) {
operator fun component1() = value[0] as A1
var arg1: A1
get() = value[0] as A1
set(value) { this.value[0] = value }
}
Joffrey
01/04/2022, 12:27 PMMethodParams1
that will decide this, not the implementation. To check a particular piece of code, you can try to decompile it to Java with the IDE.
For instance, you can try with this code:
fun main() {
val params = MethodParams1<String>(arrayOf("bob", "fred"))
println(params.component1())
println(params.arg1)
}
Which is decompiled this way:
public final class ValueClassExampleKt {
public static final void main() {
Object[] params = MethodParams1.constructor-impl(new Object[]{"bob", "fred"});
Object var1 = MethodParams1.component1-impl(params);
System.out.println(var1);
var1 = MethodParams1.getArg1-impl(params);
System.out.println(var1);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
}
As you can see, the array is not boxed into a MethodParams1
instance.Joffrey
01/04/2022, 12:29 PMdave08
01/04/2022, 12:34 PMdave08
01/04/2022, 12:35 PMObject[] params = MethodParams1.constructor-impl(new Object[]{"bob", "fred"});
Object var1 = MethodParams1.component1-impl(params);
dave08
01/04/2022, 12:35 PMdave08
01/04/2022, 12:36 PMObject var1 = MethodParams1.component1-impl(params);
is the function (even though I'm really not doing much there...)dave08
01/04/2022, 12:37 PMObject[] params = MethodParams1.constructor-impl(new
dave08
01/04/2022, 12:48 PM@NotNull
public static Object[] constructor_impl/* $FF was: constructor-impl*/(@NotNull Object[] value) {
Intrinsics.checkNotNullParameter(value, "value");
return value;
}
dave08
01/04/2022, 12:48 PMdave08
01/04/2022, 12:54 PM@JvmInline
value class MethodParamsO<A1>(val value: Array<Any?>) {
inline operator fun component1() = value[0] as A1
var arg1: A1
inline get() = value[0] as A1
inline set(value) { this.value[0] = value }
}
fun process(paramsO: MethodParamsO<String>, block: (MethodParamsO<String>) -> Unit) {
block(paramsO)
}
fun main() {
val params = MethodParamsO<String>(arrayOf("this", 0))
process(params) { (foo) ->
println("process $foo")
}
println(params.arg1)
params.arg1 = "this2"
println(params.arg1)
}
dave08
01/04/2022, 12:54 PMpublic static final void process_IC3gMN4/* $FF was: process-IC3gMN4*/(@NotNull Object[] paramsO, @NotNull Function1 block) {
Intrinsics.checkNotNullParameter(paramsO, "paramsO");
Intrinsics.checkNotNullParameter(block, "block");
block.invoke(MethodParamsO.box-impl(paramsO));
}
public static final void main() {
Object[] params = MethodParamsO.constructor-impl(new Object[]{"this", 0});
process-IC3gMN4(params, (Function1)null.INSTANCE);
int $i$f$getArg1 = false;
Object var1 = params[0];
System.out.println(var1);
Object value$iv = "this2";
int $i$f$setArg1 = false;
params[0] = value$iv;
$i$f$getArg1 = false;
var1 = params[0];
System.out.println(var1);
}
dave08
01/04/2022, 12:57 PMpublic static final Object component1_impl/* $FF was: component1-impl*/(Object[] $this) {
int $i$f$component1 = 0;
return $this[0];
}
Is there any way to avoid that @Joffrey?Joffrey
01/04/2022, 1:01 PMMethodParams1.constructor-impl
That's not being boxed?
Doesn't box, but calls that method...Regarding
constructor-impl
, this is the body of the MethodParams1
constructor. This is what allows you to write initialization code in init
blocks in your value class. This is really not a concern at all for performance.
it seems like a new instance IS still being created but not used?Only one instance of the array is created, it's theObject[] params = MethodParams1.constructor-impl(new
arrayOf
function that creates the initial array, but the body of constructor-impl
does not return a new array, it returns the same array.Joffrey
01/04/2022, 1:04 PMSeems like this usage (with componentX decomposition) seems to get boxedThe reason for boxing in your snippet is not due to decomposition methods, it's because you're using
MethodParams0
as a generic type argument in Function1
(because you use it in block: (MethodParamsO<String>) -> Unit
.
Avoiding boxing is not easy. As a rule of thumb, boxing will happen each time you use the value class as "another type" - this means each time it's assigned to a variable of a parent type, or used in a generic type where a generic type parameter is expected (like in Function1.invoke(T)
)dave08
01/04/2022, 1:06 PMdave08
01/04/2022, 1:07 PMJoffrey
01/04/2022, 1:10 PMdave08
01/04/2022, 1:12 PMdave08
01/04/2022, 1:14 PMJoffrey
01/04/2022, 1:14 PMJoffrey
01/04/2022, 1:14 PMif anyways there's no way to do this with value classes without them getting boxedI don't see one, but maybe I'm missing something
dave08
01/04/2022, 1:24 PMephemient
01/04/2022, 4:51 PMephemient
01/04/2022, 4:52 PM