1

I'm trying to create a Map that contains generic-parameterized types. For example:

abstract class Foo {
 companion object {
  val fooInjectors = HashMap<Class<T: Foo>, Injector<T: Foo>>()
 }
}

The idea is to have fooInjectors (which would be static in Java or in a companion object in Kotlin) contain a cache of sub-classes of Foo and their corresponding Injector.

Unfortunately, I can't get this to compile. I'd very much appreciate it if someone would help me figure out the syntax for this!

2
  • Do you mean <out Foo>? If you want covariance, you'll have to use that Commented Jun 17, 2020 at 20:17
  • A Map can’t have a different generic type for each entry. You’ll just have to make the Class and Injector types just plain Foo and make the map private. You can expose a getter that casts the value you get out of the map. Commented Jun 17, 2020 at 20:19

1 Answer 1

2

As far as I know, you are trying to do something that is impossible in Kotlin. The companion object is a singleton and it doesn't make sense to generify a singleton as there will not be any further objects created hence generic types are irrelevant. So you can't generify the property you declared because it's in the companion object.

However, one way you could make this working is using a backing function. This backing function should annotate with declaration-site variance.

This simply means we tell the compiler that we only return a type T from the method (and don't consume). That allows us to use subtypes and the supertype of the T if required. This is called covariance.

You can look at the docs to understand it further - https://kotlinlang.org/docs/reference/generics.html#declaration-site-variance

Here's what I meant.

interface Injector<T>
class InjectorImpl<T> : Injector<T>

abstract class Foo {
    companion object {
        val fooInjectors = createMap<Foo>()

        private fun <T> createMap(): HashMap<Class<out T>, Injector<out T>> {
            return HashMap()
        }
    }
}

class Bar: Foo()

object Runner {
    @JvmStatic
    fun main(args: Array<String>) {
        Foo.fooInjectors[Bar::class.java] = InjectorImpl<Bar>()
        Foo.fooInjectors[Foo::class.java] = InjectorImpl<Bar>()
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you very much!
Glad it helped.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.