How to specify that an abstract method's return type is the subclass's type

StackOverflow https://stackoverflow.com/questions/23592596

  •  20-07-2023
  •  | 
  •  

Frage

How, in an abstract class, can I specify that a method's return value has the same type as the concrete class that it's a member of?

For example:

abstract class Genotype {
  def makeRandom(): Genotype  // must return subclass type
  def mutate(): Genotype      // must return subclass type
}

I'd like to say that whenever you call mutate() on a concrete Genotype class, you're sure to get back another instance of the same Genotype class.

I'd prefer not to use a type parameter in the manner of Genotype[SpecificGenotype259], since that type parameter is likely to proliferate all over the code (also, it's redundant and confusing). I'd prefer to define concrete Genotype classes by extending from various traits.

War es hilfreich?

Lösung 3

I recommend a parameterized module for this situation:

trait GenotypeSystem {
  type Genotype <: GenotypeLike

  trait GenotypeLike {
    def makeRandom(): Genotype
    def mutate(): Genotype
  }
}

// Example implementation
object IntGenotypeSystem extends GenotypeSystem {
  case class Genotype(x: Int) extends GenotypeLike {
    def makeRandom() = copy(x = Random.nextInt(10))
    def mutate(): Genotype = copy(x = x + Random.nextInt(3) - 1)
  }
}

// Example abstract usage
def replicate(gs: GenotypeSystem)(g: gs.Genotype, n: Int): Seq[gs.Genotype] =
  Seq.fill(n)(g.mutate())

This approach lends itself easily to future modification and extension, such as adding other types to the GenotypeSystem.

Andere Tipps

You need F-bounded polymorphism:

abstract class Genotype[T <: Genotype[T]] {
  def makeRandom(): T  
  def mutate(): T      
}

class ConcreteGenotype extends Genotype[ConcreteGenotype] {
  def makeRandom(): ConcreteGenotype = ???
  def mutate(): ConcreteGenotype = ???
}
abstract class Genotype {
  type T <: Genotype
  def makeRandom(): T  
  def mutate(): T      
}

Is this what you want?

Maybe using covariant return types will work in your case?

abstract class Genotype {
  def makeRandom(): Genotype  // must return subclass type
  def mutate(): Genotype      // must return subclass type
}

class ConcreteGenotype(val a : Int) extends Genotype {
  override def makeRandom(): ConcreteGenotype = new ConcreteGenotype(a + 1)
  override def mutate(): ConcreteGenotype = new ConcreteGenotype(a + 2)
}

def useAbstractGenotype(g : Genotype) = g.mutate

val cg = new ConcreteGenotype(0) // cg: ConcreteGenotype = ConcreteGenotype@6c1c19c6
cg.mutate                        // res2: ConcreteGenotype = ConcreteGenotype@3fc1058a
useAbstractGenotype(cg)          // res3: Genotype = ConcreteGenotype@13515ded
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top