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

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

  •  20-07-2023
  •  | 
  •  

Question

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.

Était-ce utile?

La solution 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.

Autres conseils

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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top