Scala 中的类型参数和继承

Type parameters and inheritance in Scala

有没有一种简单的方法可以 return 覆盖方法中的具体类型?那么如何创建一个具体实现的实例呢?并调用在具体 class 中实现的链式方法,所以它们 return 也是正确的类型吗?我有一个解决方案(基于),但我觉得这些事情应该更简单。

类似的问题还有很多,但是每个人的情况都有些不同,所以这里再举一个例子(https://github.com/valdanylchuk/saiml/tree/master/src/main/scala/saiml/ga的缩写)。回复时,如果可能,请检查整个代码块是否根据您建议的更改进行编译,因为存在细微的级联效应。例如,我无法使用 "curiously recurring template pattern" 来完成这项工作(并不是说我觉得它更好)。

import scala.reflect.ClassTag
import scala.util.Random

abstract class Individual(val genome: String) {
  type Self
  def this() = this("")  // please override with a random constructor
  def crossover(that: Individual): Self
}
class HelloGenetic(override val genome: String) extends Individual {
  type Self = HelloGenetic
  def this() = this(Random.alphanumeric.take("Hello, World!".length).mkString)
  override def crossover(that: Individual): HelloGenetic = {
    val newGenome = this.genome.substring(0, 6) + that.genome.substring(6)
    new HelloGenetic(newGenome)
  }
}
class Population[A <: Individual {type Self = A} :ClassTag]( val size: Int,
    tournamentSize: Int, givenIndividuals: Option[Vector[A]] = None) {
  val individuals: Vector[A] = givenIndividuals getOrElse
    Vector.tabulate(size)(_ => implicitly[ClassTag[A]].runtimeClass.newInstance.asInstanceOf[A])
  def tournamentSelect(): A = individuals.head  // not really, skipped
  def evolve: Population[A] = {
    val nextGen = (0 until size).map { _ =>
      val parent1: A = tournamentSelect()
      val parent2: A = tournamentSelect()
      val child: A = parent1.crossover(parent2)
      child
    }.toVector
    new Population(size, tournamentSize, Some(nextGen))
  }
}
class Genetic[A <: Individual {type Self = A} :ClassTag](populationSize: Int, tournamentSize: Int) {
  def optimize(maxGen: Int, maxMillis: Long): Individual = {
    val first = new Population[A](populationSize, tournamentSize)
    val optPop = (0 until maxGen).foldLeft(first) { (pop, _) => pop.evolve }
    optPop.individuals.head
  }
}

CRTP 版本为

abstract class Individual[A <: Individual[A]](val genome: String) {
  def this() = this("")  // please override with a random constructor

  def crossover(that: A): A
}
class HelloGenetic(override val genome: String) extends Individual[HelloGenetic] {
  def this() = this(Random.alphanumeric.take("Hello, World!".length).mkString)
  override def crossover(that: HelloGenetic): HelloGenetic = {
    val newGenome = this.genome.substring(0, 6) + that.genome.substring(6)
    new HelloGenetic(newGenome)
  }
}
class Population[A <: Individual[A] :ClassTag]( val size: Int,
    tournamentSize: Int, givenIndividuals: Option[Vector[A]] = None) {
  val individuals: Vector[A] = givenIndividuals getOrElse
    Vector.tabulate(size)(_ => implicitly[ClassTag[A]].runtimeClass.newInstance.asInstanceOf[A])
  def tournamentSelect(): A = individuals.head  // not really, skipped

  def evolve: Population[A] = {
    val nextGen = (0 until size).map { _ =>
      val parent1: A = tournamentSelect()
      val parent2: A = tournamentSelect()
      val child: A = parent1.crossover(parent2)
      child
    }.toVector
    new Population(size, tournamentSize, Some(nextGen))
  }
}
class Genetic[A <: Individual[A] :ClassTag](populationSize: Int, tournamentSize: Int) {
  def optimize(maxGen: Int, maxMillis: Long): Individual[A] = {
    val first = new Population[A](populationSize, tournamentSize)
    val optPop = (0 until maxGen).foldLeft(first) { (pop, _) => pop.evolve }
    optPop.individuals.head
  }
}

哪个compiles。对于创建实例,我建议只传递函数:

class Population[A <: Individual[A]](val size: Int,
    tournamentSize: Int, makeIndividual: () => A, givenIndividuals: Option[Vector[A]] = None) {
  val individuals: Vector[A] = givenIndividuals getOrElse
    Vector.fill(size)(makeIndividual())
  ...
}

如果你想隐式传递它们,你可以很容易地这样做:

trait IndividualFactory[A] {
  def apply(): A
}

class HelloGenetic ... // remove def this() 
object HelloGenetic {
  implicit val factory: IndividualFactory[HelloGenetic] = new IndividualFactory[HelloGenetic] {
    def apply() = new HelloGenetic(Random.alphanumeric.take("Hello, World!".length).mkString)
  }
}
class Population[A <: Individual[A]](val size: Int,
    tournamentSize: Int, givenIndividuals: Option[Vector[A]] = None)
    (implicit factory: IndividualFactory[A]) {
  val individuals: Vector[A] = givenIndividuals getOrElse
    Vector.fill(size)(factory())
  ...
}