Using @JvmField in companion objects of inline classes

Using Kotlin 1.5.21, I’m trying to add a constant to the companion object, like Integer.ZERO:

@JvmInline
value class MyInt(val value: Int) {
    companion object {
         @JvmField val ZERO = MyInt(0)
    }
}

However, I get the “JvmField cannot be applied to a property of an inline class type” error.

But it’s not really “a property of an inline class type”! It’s a property of the companion object of an inline class type! Can’t the compiler just add a static field to the boxing class? Is it a bug in the compiler or am I missing some reasoning behind this restriction? Googling the error message doesn’t yield anything useful, short of a few 3rd-party GitHub issues.

1 Like

The error isn’t due to the fact that ZERO is inside the inline class, but that ZERO’s type is MyInt. The issue is that representing an inline class as a field is a bit ambiguous, especially when you want it solely for JVM compatibility. For example, this works:

@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
@JvmInline
value class MyInt(val value: Int) {
    companion object {
         @JvmField val ZERO = 0
    }
}

but this doesn’t:

object Test {
         @JvmField val ZERO = MyInt(0)
}

Ah, I see. That’s why it says “of an inline class type”, and not just “of an inline class”.

I see that Java interop is a real mess with inline classes anyway, and I guess it’s expected, given that Java has no idea of these types and can’t provide unambiguous signatures or autoboxing. Gotta think about it for a while…

1 Like

I’d say that single-field value classes should really be used only when you need that optimisation, and so because you’re optimising already, then it’s entirely reasonable to rewrite some Java code into Kotlin so that there’s no interop difficulties for those single-field value classes

Fair point, but it doesn’t always work like that.

Imagine a library that is used by 10 projects. Then you figure out that one of these is creating millions of instances of a class from the common library. Suppose that this project is even 100% Kotlin already, so turning that class into a value class sounds perfectly reasonable. But then you’d break the other 9 projects, some of which contain a lot of legacy Java code. They don’t really need that optimization, because they don’t create that many instances at all.

Ideally, I’d like Java to transparently use boxing in such cases. In this example, I’d like Kotlin to create two fields: a private one containing the primitive value, with a mangled name, and a public one for Java, containing the boxed value.

Same goes for member functions, which are currently unusable from Java at all, as far as I can see. Or value class returning functions…

1 Like