David Kubecka
09/05/2023, 2:48 PMprocessBs
work, especially I would like to keep the List<T>
return type. Is it possible? Code and playground link in thread.David Kubecka
09/05/2023, 2:48 PMDavid Kubecka
09/05/2023, 2:49 PMsealed class A<T : A<T>>
class A1 : A<A1>()
class A2 : A<A2>()
sealed class B<T : A<T>> {
abstract fun create(): T
}
class B1 : B<A1>() {
override fun create() = A1()
}
class B2 : B<A2>() {
override fun create() = A2()
}
fun <T : A<T>> processBs(bs: Set<B<T>>): List<T> =
bs.map { it.create() }
fun main() {
val bs = setOf(B1(), B2())
processBs(bs)
}
Sam
09/05/2023, 2:52 PMout
variance to both generic types will work:
sealed class A<out T: A<T>>
sealed class B<out T: A<T>>
Javier
09/05/2023, 2:52 PMsealed class A<out T : A<T>>
sealed class B<out T : A<T>>
Javier
09/05/2023, 2:52 PMDavid Kubecka
09/05/2023, 2:52 PMType mismatch.
Required:
A<Nothing>
Found:
A<*>
If I follow the IDE advice and change the signature to
<T : A<T>> processBs(bs: Set<B<out A<*>>>): List<T>
then it fails on the function body with
Type mismatch.
Required:
List<T>
Found:
List<A<*>>
Then I'm advised to change the signature again to
fun <T : A<T>> processBs(bs: Set<B<out A<*>>>): List<A<*>>
and now it works but it violates my requirement that the function returns List<T>
.
Can make it work?ephemient
09/05/2023, 2:53 PMbs
is?ephemient
09/05/2023, 2:54 PMB
as invariant, the common parent of B1
and B2
is B<*>
David Kubecka
09/05/2023, 2:57 PMB<out A<*>>
^David Kubecka
09/05/2023, 2:58 PMGenericContainer
implementation, e.g. public class KafkaContainer extends GenericContainer<KafkaContainer>
.
What now? Am I screwed? 🙂Sam
09/05/2023, 3:02 PMsealed class B<out T : A<out T>>
and change the processBs
signature to
fun <T : A<out T>> processBs(bs: Set<B<T>>): List<T> =
David Kubecka
09/05/2023, 3:06 PMephemient
09/05/2023, 3:06 PMSam
09/05/2023, 3:07 PMDavid Kubecka
09/05/2023, 3:08 PMDavid Kubecka
09/05/2023, 3:11 PMSam
09/05/2023, 3:14 PMRiccardo Lippolis
09/05/2023, 8:45 PMDavid Kubecka
09/06/2023, 11:58 AMabstract class TestContainerRegistrar<T : GenericContainer<out T>> {
protected abstract fun initializeContainer(environment: Environment): T
protected open fun initRunningContainer(container: T, environment: Environment) {}
protected open fun resetRunningContainer(container: T, environment: Environment) {}
}
To translate it to the naming in the original example, GenericContainer
corresponds to class A,
TestContainerRegistrar
to class B
and the B.create
method to initializeContainer
. For illustration purposes, I've also included the other methods where the generic parameter is in the out position (therefore changing the class signature to TestContainerRegistrar<out T : GenericContainer<out T>>
is not possible).
One obvious solution would be to split the TestContainerRegistrar
class into two, each containing just the covariant, resp. contravariant methods. But this doesn't make much sense to me. Any other ideas?Sam
09/06/2023, 12:48 PMinitializeContainer
function return an object which exposes both the container and its init
and reset
functions?David Kubecka
09/06/2023, 1:35 PMGenericContainer
but AFAIK this can't be made polymorphic.