Heyo! I've got a KMP project an am using value cla...
# multiplatform
m
Heyo! I've got a KMP project an am using value classes, but cannot for the life of me figure out how to require their use in Java from an interface method.
Copy code
sealed interface Key {
    fun descriptor(address: Address): String
}

@JvmInline
value class Address(@JvmField val value: String)
Calling From Java:
Copy code
public class SomeClass {
    public void someFunction(Key key) {
        // Key.descriptor accepts a String, not an Address
        String descriptor = key.descriptor("");

        Address address = new Address("someaddress");

        // key.descriptor does not accept an Address and IDE shows error
        String descriptor2 = key.descriptor(address);
    }
}
Any Ideas?
g
Hello, probably this is what you are looking for https://kotlinlang.org/docs/inline-classes.html#calling-from-java-code
m
In Java, you would call
SomeClass().someFunction("someaddress")
-- value types are not enforced by the JVM
m
With some ingenuity, I was able to remove value classes from my public APIs using sealed interfaces and operator function
Copy code
sealed interface Port {
    val value: Int

    companion object {
        @JvmStatic
        @Throws(IllegalArgumentException::class)
        operator fun invoke(port: Int): Port {
            return RealPort(port)
        }
    }
}

@JvmInline
private value class RealPort(override val value: Int): Port {
    init {
        require(value in 0..65535) {
            "Invalid port range"
        }
    }
}
And calling from Java:
Copy code
Port port = Port.invoke(65536); // Will throw IllegalArgumentException
This way, all my interfaces that take a
Port
now compile to the sealed interface, instead of the primitive type.
Ty George and Michael for your responses!
K 1
m
@Matt Nelson interesting approach. Worth noting that this has two hits against it, in addition to the obvious positives: 1) far less efficient 2) difficult to read
At this point, why not do it like this:
data class Port(val value: Int)
?
There will still be a boxing/unboxing on the JVM side for all of this, so doing
data class Port(...)
would have the same affect I'd think.
m
Yeah, I'm still playing with things to get it right. Performance wise, data classes are very bad. See my test results HERE
m
What application are you writing that is using so many port numbers you have to worry about inefficiency?
m
It's a wrapper for Tor
m
You know, that's just about the one valid answer you could have given to that question right?
m
lol, yes.
m
I still think it'd be fully wrapped on the java side, and maybe on the kotlin side too. It might lose the perceived benefit. You should check out the bytecode.
Perhaps throw an example together that calls it from the java side and measure the time?
m
Decompiled byte code shows it's boxed.
m
What are your memory constraints? Caching every instance is only 0.5 MB
m
As minimal as possible, as running Tor on Android is a hog.
m
It does bear asking the question -- why not just use ints straight up? Maybe create a
typealias
if you're feeling curious.
I'm all for kotlinic code but here it feels like a good place to optimize
m
For sure, but Tor is very picky about the strings that it receives via it's control port. Using value classes is a very low cost way to offload knowledge library consumers have to have when interacting with the APIs (how many people know that a v3 onion address is a 56 character base32 encoded lowercase string?) by handling all of that and returning a wrapper, it can be passed around everywhere straight from the controller response all the way down to the DB. Very low friction on library consumer side as the APIs "guide" things before even making it to the request being sent to the controller, so.
m
@Matt Nelson my friend, I am forever grateful to you. I have used this to create a
WrappedLong
implementation that is compatible with JS but performs very quickly on JVM. You are a genius.
🔥 1
m
Created a library for this specific issue of Kotlin
value class
not compiling to platform code. https://github.com/05nelsonm/component-value-clazz