Inline classes tedious to use considering Java interop


#1

I’m a big fan of type safety and really like the new inline classes feature in 1.3. Unfortunately it gets really tedious to add them to functions that are used from both Kotlin and Java code. I find myself creating many facades like this:

// for Java
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated(message = "unchecked")
fun givePlayerPoints(playerId: Long, points: Int, type: PointTransactionType, roomId: Long?) =
    givePlayerPoints(PlayerId(playerId), PointChange(points), type, if (roomId == null) null else RoomId(roomId))

// PlayerId, PointChange and RoomId are inline classes
fun givePlayerPoints(playerId: PlayerId, points: PointChange, type: PointTransactionType, roomId: RoomId?): PointTransaction {
    ....

I would imagine that this use case will become pretty common, and would much appreciate a usability improvement for this problem. I suppose a simple annotation in the @JvmXxx category would be in order.

Also, I wouldn’t really mind using the boxed types from Java code, taking a little performance hit, to circumvent this issue, but this is not possible because of the name wrangling introduced in 1.3-M2.


#2

This is the real issue IMO, but I can’t think of a better way than the facade/overload you write. If the names weren’t mangled, the Java callers wouldn’t preserve/know the type safety (i.e. you’d call a Uint w/ an Int). If they always accepted boxed types, then you would lose that performance of inline types (though it happens anyways in some generic cases).

I personally think at this point the cost of requiring you to hand-write Java-visible wrappers is preferred over annotation-based conditional exposure changes (has binary compatibility issues across JARs if you change after deployment too). The question is if it occurs so often (i.e. exposing inline classes to Java-side instantiators) that it deserves special treatment…I’d hope not, and/or I’d encourage no longer making them inline classes as opposed to single-val wrapper classes since you’re losing your benefit at the interop boundary anyways.


#3

Not sure I agree with that. Imagine writing a library in Kotlin. If it is used by a kotlin project you would want to have those wrappers as inline classes, also you might want to use inline classes internally.
But you would also need java interop, so I think having an annotation creating the java visible methods is useful.
One way to solve type safety is to make those calls boxing. This way you loose some performance at the point you interact with java but don’t stop interop entirely without having to write boilerplate code. There might be many situations where having a fast interop boundary is not that important but you still want better performing values deeper in the stack.

As another option I could imagine having 2 annotations. One would generate the function using boxing and another allowing java to call a UInt function with int trusting the programmer to pass correct values in order to improve performance. This might be the best way.