i've been playing with inline classes and noticed ...
# announcements
b
i've been playing with inline classes and noticed when doing some CPU profiling i was spending some time in what seems to be an init method of my inline class. i thought they'd just be compiled to the underlying type?
RtpSequenceNumber
is my inline class here, it wraps an
Int
h
They are boxed when required, generics for example
https://typealias.com/guides/inline-classes-and-autoboxing/ gives a nice overview of when/why they are boxed
b
i'll take a look at that, thanks
so it looks like the inline class is getting boxed when doing
!=
, as in
if (inlineClassInstance1 != inlineClassInstance2
...but from that article i wouldn't think that would need boxing? maybe i misread. i do have a custom
compareTo
implemented for it, is that why?
h
Ya I believe so - it's like the Amount example in that article
b
i thought that was because of the inheritance bit (which i don't have)
also i'm not using it in any container here
h
Did some testing, they are always boxed when you use
!=
So even this boxes them:
Copy code
inline class Test(val n: Int)

fun main() {
    val t1 = Test(1)
    val t2 = Test(2)

    println(t1 != t2)
}
To avoid it compare the wrapped value instead:
Copy code
inline class Test(val n: Int)

fun main() {
    val t1 = Test(1)
    val t2 = Test(2)

    println(t1.n != t2.n)
}
It's because
equals
takes an `Object`/`Any` so it has to be boxed
b
thanks brian! that's good to know. so maybe if i override equals as well in the inline class i can avoid it
h
That won't help unfortunately. equals is defined as
fun equals(other: Any?): Boolean
, since it takes
Any?
as a parameter, it'll be boxed.
b
damn
h
Using
!=
on the wrapped
Int
is the only way to avoid it. That means it can't be
private
though which kinda sucks
b
yeah...or could use some custom equals fun i guess?
h
Yeah looks like that'd work. This doesn't box:
Copy code
inline class Test(val value: Int) {
    infix fun eq(other: Test): Boolean = value == other.value
}

fun main() {
    val t1 = Test(1)
    val t2 = Test(2)
    
    println(t1 eq t2)
}
b
cool. i don't think the perf hit in this case feels like a dealbreaker yet, but maybe that's a decent workaround
though it would be really nice for inline classes to handle this case, as it seems like it'd be a common use
h
Yeah, it'd be nice. Maybe they'll support it in the future 🤞
b
hmm, on this page: https://github.com/Kotlin/KEEP/blob/master/proposals/inline-classes.md i see a reference to this:
Copy code
// Reserved method for specialized equals to avoid boxing
    public final static equals-impl0(II)Z
h
Huh maybe it's a bug then. I see that they are generating a
equals-impl
and delegating to that:
Copy code
public static boolean equals_impl(int var0, @Nullable Object var1)
public boolean equals(Object var1) {
      return equals-impl(this.value, var1);
   }
but
main
is clearly boxing:
boolean var2 = Intrinsics.areEqual(Test.box-impl(t1), Test.box-impl(t2));
b
cool, maybe it's just a bug then
was unable to find anything existing--some close, maybe i misunderstood them--but i filed https://youtrack.jetbrains.com/issue/KT-30785
👍 1