Nathan Osman
Nathan Osman

Reputation: 73295

How to create a class with a method that returns a subtype with a type parameter in Kotlin?

I am struggling to understand how generics / type parameters work in Kotlin. I am working on a (fairly complex) app that is throwing some very confusing error messages during compilation. I've simplified things below to the minimum amount of code that will reproduce the error.

I have an interface and two abstract classes:

interface Player {
    fun play()
}

abstract class Device <T : Player> {
    abstract fun getPlayer(): T
}

abstract class DeviceFactory {
    abstract fun <T : Player> create(): Device<T>
}

The problem arises when I try to create a class that implements DeviceFactory:

class MyDeviceFactory : DeviceFactory() {
    
    class MyPlayer : Player {
        override fun play() {
            println("[sound plays here]")
        }
    }
    
    class MyDevice : Device<MyPlayer>() {
        override fun getPlayer() = MyPlayer()
    }
    
    override fun create() = MyDevice()
}

The last line of code is where the problem arises, yielding the following error message:

Conflicting overloads: public open fun create(): MyDeviceFactory.MyDevice defined in MyDeviceFactory,
public abstract fun  create(): Device defined in DeviceFactory

Thinking that maybe the problem was the missing type parameter, I tried this instead:

override fun <T : Player> create() = MyDevice()

Now I have a different error message:

Return type of 'create' is not a subtype of the return type of the overridden member
'public abstract fun  create(): Device defined in DeviceFactory'

This doesn't make sense — MyDevice is a subtype of Device<T>, right? To be sure, I tried making the return type explicit:

override fun <T : Player> create(): Device<T> = MyDevice()

No dice:

Type mismatch: inferred type is MyDeviceFactory.MyDevice but Device was expected

How can I create a class that derives from DeviceFactory and returns an instance of MyDevice?

Upvotes: 1

Views: 630

Answers (1)

ChristianB
ChristianB

Reputation: 2690

You need to declare the type for DeviceFactory on it's class:

abstract class DeviceFactory<T : Player> {
    abstract fun create(): Device<T>
}

Then you can define a factory that returns a concrete Player:

class MyDeviceFactory : DeviceFactory<MyPlayer>() {
    override fun create(): Device<MyPlayer> = MyDevice()
}

Upvotes: 1

Related Questions