类型与抽象类型不匹配

时间:2016-03-25 02:40:31

标签: scala abstract-type

我正在使用抽象类型,并且在让Scala编译器统一两个应该相同的类型时遇到问题。这些是我的定义:

trait Chromosome {
  type Gene

  val size : Int

  def apply(idx : Int) : Gene

  def update(idx : Int, g : Gene)

  def indices = Range(0, size-1)
}


trait Individual {
  type Fitness
  implicit protected val ord: Ordered[Fitness] = implicitly[Ordered[Fitness]]

  val chromosome : Chromosome

  def size : Int = chromosome.size

  def apply(idx : Int) : chromosome.Gene = chromosome.apply(idx)
  def update(idx : Int, g : chromosome.Gene ) = chromosome.update(idx, g)

  protected var _fitness : Fitness = _
  def fitness : Fitness = _fitness
  def fitness_=(f : Fitness):Unit = _fitness = f
}


case class ArrayChromosome(size : Int) extends Chromosome {
  implicit protected val tag : ClassTag[Gene] = implicitly[ClassTag[Gene]]

  protected val xs : Array[Gene] = new Array[Gene](size)

  def apply(idx : Int) = xs(idx)

  def update(idx : Int, g : Gene) = xs(idx) = g
}


abstract class ArrayIndividual(size : Int) extends Individual {
  val chromosome  = ArrayChromosome(size)
}


class MyIndividual(size : Int) extends ArrayIndividual(size) {
  type Gene = Int
  type Fitness = Double
}


object Test extends App {
  val i1 = new MyIndividual(10)

  i1.fitness = 10.5
  i1.chromosome(0) = 6
  print(i1)
}

即问题出在这一行:

i1.chromosome(0) = 6

,类型错误是:

Error:(75, 22) type mismatch;
 found   : Int(6)
 required: abstractTypes.Test.i1.chromosome.Gene
  i1.chromosome(0) = 6

似乎编译器无法将i1.chromosome.GeneInt统一起来。

我有两个问题:

  1. 是否可以帮助编译器统一这两种类型?
  2. 现在让我假设我删除了违规行,并且我已为toString正确定义了Individual。运行java.lang.NullPointerException时,为什么会收到Test。似乎该错误与Gene ClassTag有关,但我不确定。
  3. 响应代码至@ som-snytt:

    abstract class ArrayIndividual(size : Int) extends Individual {
      type Gene
      implicit protected def tag2: ClassTag[Gene]
    
      val chromosome  = new ArrayChromosome(size) {
        type Gene = ArrayIndividual#Gene
        protected val tag : ClassTag[Gene] = tag2
      }
    }
    
    class MyIndividual(size : Int) extends ArrayIndividual(size) {
      type Fitness = Double
      type Gene = Int
      implicit protected val tag2 : ClassTag[Gene] = implicitly[ClassTag[Gene]]
    
      override def toString = s"${super.toString} $chromosome"
    }
    

1 个答案:

答案 0 :(得分:1)

在Scala中,类型不是全局范围的。您已将Gene作为Chromosome类型的成员编写,但您尝试在Individual的子类中定义它(其中包含“Chromosome,而不是“是一个”Chromosome)。

您可以选择声明和定义的位置,但它必须位于同一层次结构中,就像覆盖术语成员(如方法)时一样。

individual.Gene与您示例中的chromosome.Gene无关。

无论如何,将类型定义移动到ClassTag所需的位置:

case class ArrayChromosome(size : Int) extends Chromosome {
  type Gene = Int
  implicit protected val tag : ClassTag[Gene] = implicitly[ClassTag[Gene]]

噢,我只是注意到你是在递归地定义你的隐含。隐含在RHS上使用你所定义的隐含。所以不要这样做。编译器自己召唤隐式。

但是如果你确实需要在一个没有修复别名类型的超类中提供一个隐式标记,你可以使用一个抽象方法来提供一个可以在一个具体类中实现的类标记。

例如,让Individual子类指定具体的Chromosome

abstract class ArrayChromosome(val size : Int) extends Chromosome {
  //type Gene = Int
  //implicit protected val tag : ClassTag[Gene] = implicitly[ClassTag[Gene]]
  implicit protected def tag: ClassTag[Gene]

  protected lazy val xs : Array[Gene] = new Array[Gene](size)

  def apply(idx : Int) = xs(idx)

  def update(idx : Int, g : Gene) = xs(idx) = g
}


abstract class ArrayIndividual(size : Int) extends Individual {
  //val chromosome  = ArrayChromosome(size)
  val chromosome  = new ArrayChromosome(size) {
    type Gene = Int
    protected val tag = implicitly[ClassTag[Gene]]
  }
}


class MyIndividual(size : Int) extends ArrayIndividual(size) {
  type Fitness = Double

  override def toString = s"${super.toString} $chromosome"
}