Any way to make this work? :slightly_smiling_face:...
# stdlib
z
Any way to make this work? 🙂
Copy code
package aggregate.common

abstract class Keyed(
    val key: String,
) {
    // Does not compile, Id is value class that wraps String 
    constructor(id: Id) : this(
        key = id.value,
    )

    constructor(keyed: Keyed) : this(
        key = keyed.key,
    )
}
c
What's the error?
z
Keyed.kt321 Platform declaration clash: The following declarations have the same JVM signature (<init>(Ljava/lang/String;)V): constructor Keyed(id: Id) defined in aggregate.common.Keyed constructor Keyed(key: String) defined in aggregate.common.Keyed
Copy code
@JvmInline
value class Id(
    val value: String = Uuid.random().toString(),
)
c
Why do you need the
String
constructor if you have an
Id
type already written?
z
I dont always use Id as the key, but often enough that itd be nice to just be able to pass it in instead of id.value
p
how often do you pass a bare string?
c
You won't be able to have both as constructors, because they're actually the same type (that's the point of
@JvmInline
).
👍🏽 1
z
28 usages with id 4 with string
p
Copy code
@JvmInline value class Id(val value: String)

abstract class Keyed(
    val key: String,
    val _marker: Unit = Unit
) {
    // Does not compile, Id is value class that wraps String 
    constructor(id: Id) : this(
        key = id.value,
    )

    constructor(keyed: Keyed) : this(
        key = keyed.key,
    )
}

fun main() {
    val myKeyed = object : Keyed("hello") {}
    println(myKeyed.key)
    
    val myKeyedById = object : Keyed(Id("world")) {}
    println(myKeyedById.key)
    
    val myKeyedByKeyed = object : Keyed(myKeyed) {}
    println(myKeyedByKeyed.key)
}
the trick is to make one of the constructors unique
z
I was afraid of that, but always worth a shot to ask ^^ I think Ill have to survive the id.value calls, only other option that I know would work is have interface Keyed and do : Keyed by id(id) or something like that.. but thats just bad code Id say
That works, Phil? 😃
p
z
Haha, very clever!
c
I would make the secondary one have the marker parameter, to avoid storing a reference in the class (though it's a cheap one)
p
the ideal would be to make the primary constructor private, but that doesn't work if you still need it
ah - sorry - doesn't need to be
val
, just
_marker: Unit = Unit
will do
2
c
Personally I'm not a fan of these kinds of "hacks" because they're visible in the API… it's "too clever"
p
which is why, if you can change the four usages of it as String to Id you'd have an easy time
1
z
I dont know what Id prefer, this is hacky yes, but it feels semantically wrong to create an id from something thats not an id, so to speak.
p
I'd prefer this hack over breaking semantics
z
Copy code
abstract class Keyed(
    val key: String,
) {
    constructor(
        id: Id,
        marker: Unit = Unit,
    ) : this(key = id.value)

    constructor(keyed: Keyed) : this(
        key = keyed.key,
    )

    constructor(first: Keyed, second: Keyed) : this(
        key = first.key + second.key,
    )
}
This is quite simple imo, and I think Ill roll with it!
Thank you for teaching me something new 😃
p
now you have a new hammer, don't treat everything as a nail 😉
z
Ill do my best 🫡
r
You can even take this one useless step further. As it stands now, the user could call
Keyed(id, Unit)
, which isn't a big deal, but a little strange. if instead you declare a new helper class
class Nope private constructor()
, you could change the constructor to
constructor(id: Id, vararg marker: Nope) ...
, and then the user couldn't pass in anything other than the ID. (I used to use this trick with
vararg x: Nothing
to force users to used named parameters back in the day, but
Nothing
has since been banned as a vararg type, and I've learned that if I think I need to force named params on the users to make things clear, it's a good sign my design is garbage)
👍🏽 1
e
Companion factory function instead of a constructor maybe?
c
@Eugen Martynov if you do that, you can't
super()
that constructor when extending the abstract class
e
Ah, it is abstract class