https://kotlinlang.org logo
#getting-started
Title
# getting-started
d

David Kubecka

12/12/2023, 2:10 PM
Why is the type of
type
Base<out Any>
and not
Base<Any>
? How to achieve the latter type?
Copy code
sealed interface Base<T>

class A : Base<Int>
class B : Base<String>

val cond = false
val type = when (cond) {
    true -> A()
    false -> B()
}
s

Sam

12/12/2023, 2:20 PM
Well, you could declare the interface as
Copy code
sealed interface Base<out T>
but that feels like it's just moving the problem 😄
d

David Kubecka

12/12/2023, 2:21 PM
Exactly. In reality the T is invariant. Am I doomed?
s

Sam

12/12/2023, 2:23 PM
Depends what you're trying to achieve, but if
Base
really is invariant in
T
then I think you've answered your own question
d

David Kubecka

12/12/2023, 2:28 PM
What I need is to write a repository dispatcher:
Copy code
override fun save(messageKey: ..., messageValue: ...) {
        val repo = selectRepo(messageKey)

        val newDocument = repo.findById(messageKey.id)
            ?.apply { lastUpdate = messageValue.lastUpdate }
            ?: messageValue

        // this fails because it requires Type in the "in" position
        repo.save(newDocument)
    }

fun selectRepo(messageKey: ...): Repository<out Type> =
   when (...) { 
      // dispatching here
   }
Perhaps there's a different way how to structure this?
Well, I can probably pass a lambda to
selectRepo
to directly perform the desired action.
s

Sam

12/12/2023, 2:33 PM
My suggestion would be to capture the type of the repository by creating a function with its own generic type parameter. It would look something like this:
Copy code
override fun save(messageKey: ..., messageValue: ...) {
    val repo = selectRepo(messageKey)
    repo.update(messageKey, messageValue)
}

private fun <T: Type> Repository<T>.update(messageKey, messageValue) {
    val newDocument = findById(messageKey.id)
        ?.apply { lastUpdate = messageValue.lastUpdate }
        ?: messageValue

    // this fails because it requires Type in the "in" position
    repo.save(newDocument)
}
It works because the
update
function knows that the type returned by
findById
and the type accepted by
save
must be the same, even if the actual type is unknown
I don't know if Kotlin has any good documentation/sources relating to generic type capture, but the concept is much the same as in Java
d

David Kubecka

12/12/2023, 2:39 PM
Yeah, I probably understand the problem: In my version repo is
Repository<out Type>
so
findById
also return
out Type
and that can't be passed to any method on the repo (because that requires
in
). I will try your suggestion,. Thanks.
Hmm, doesn't work as the problem just got transferred to
repo.update(messageKey, messageValue)
🙂
s

Sam

12/12/2023, 2:51 PM
Maybe the
?: messageValue
part is messing things up 😞. It should be a solvable problem, but an unchecked cast might be easier 😄
d

David Kubecka

12/12/2023, 2:54 PM
No, the concrete type is
messageValue: CodebookBase
and the error is on
messageValue
in
repo.update(messageKey, messageValue)
:
Copy code
Type mismatch.
Required:
CapturedType(out CodebookBase)
Found:
CodebookBase
s

Sam

12/12/2023, 2:55 PM
Yeah, that's what I thought it might be 😞
d

David Kubecka

12/12/2023, 2:56 PM
But how to even cast it? To what? 🙂
Got it. Need to unchecked-cast the
repo
. I guess there really is no clean way around this. I you find some then let me know, though 🙂 Thanks
d

Daniel Pitts

12/12/2023, 3:43 PM
If T could be either String or Int, then it must be
out
of their common base. Otherwise you could pass in a String to the Int type, or an Int to the String type, or worse yet, a
Foo
to the Int type.
The problem isn't the type system, its the conditionally having different types.
plus one 1