zoku
zoku

Reputation: 7236

Is it possible to restrict the type of a List to an implementation of an interface in Kotlin?

I have a class Stealth implementing the interface Skill and a class SoundlessMovement implementing the interface Mastery. Now I want to add the mastery SoundlessMovement to the Skill Stealth, while ensuring the Mastery to be supported by the Skill.

I want to do something like this (Pseudo-Kotlin):

interface Skill {
    val masteries: List<Mastery<Skill>> // Mastery linked to type Skill
}

and

interface Mastery<Skill> { // Every Mastery must be compatible with only one Skill
    val masteryName: String
}

with implementations:

class Stealth : Skill {
    override val masteries: ArrayList<Mastery<Stealth>() // Any Mastery bound to Stealth
}

and

class SoundlessMovement : Mastery<Stealth> { // Is a Mastery compatible with Stealth
    override val masteryName = "Soundless Movement"
}

The goal is to make sure that only compatible masteries can be added to a skill. Is something like this even possible? And if yes, how could it be implemented in Kotlin?

Upvotes: 0

Views: 144

Answers (1)

Alberto S.
Alberto S.

Reputation: 7649

I don't know if I really understood you but I think this piece of code solves your problem:

interface Mastery<Skill> {
    val masteryName: String
}

interface Skill<Self : Skill<Self>> {                              // forces every Skill to bound to itself
    val masteries: List<Mastery<Self>>
}

abstract class Stealth : Skill<Stealth> {
    override val masteries = ArrayList<Mastery<Stealth>>()
}

class SoundlessMovement : Mastery<Stealth> {         // a Stealth mastery
    override val masteryName = "Soundless Movement"
}

class SoundlessMovement2 : Mastery<Stealth> {        // another Stealth mastery
    override val masteryName = "Soundless Movement 2"
}

abstract class Archery : Skill<Archery> {         
    override val masteries = ArrayList<Mastery<Archery>>()
}

class SureShot : Mastery<Archery> {                  // some other mastery
    override val masteryName = "Sure shot 2"
}

fun main(args: Array<String>) {

    var list = ArrayList<Mastery<Stealth>>()

    list.add(SoundlessMovement())            // compiles
    list.add(SoundlessMovement2())           // compiles
    list.add(SureShot())                     // Doesn't compile
}

The key here is to use Skill<Self> to bound the current implementations. Note also that I've changed class Stealth ... to abstract class Stealth ... since you're defining the mastery type. Another option could be using Kotlin's sealed class

Beware that Self is not a reserved word nor anything similar. The code will also work if you just change it to just T. I chose to use Self just to make you aware what type you should put there

Upvotes: 3

Related Questions