"MyType"问题:我是否必须在Scala中使用抽象类型(或泛型)来返回实际的类?

oll*_*erg 9 generics scala

我不确定是否有更好的方法:

trait Animal {
  val name: String
  val weight: Int

  type SubAnimal <: Animal

  def updateName(n: String) = returnMe(n, this.weight)
  def updateWeight(w: Int) = returnMe(this.name, w)
  // Abstract protected method
  protected def returnMe(n: String, w: Int): SubAnimal
}

case class Dog(name: String, weight: Int) extends Animal {
  type SubAnimal = Dog
  override def returnMe(n: String, w: Int): Dog = Dog("Dog: " + name, w)
}
case class Cat(name: String, weight: Int) extends Animal {
  type SubAnimal = Cat
  override def returnMe(n: String, w: Int): Cat = Cat("Cat: " + name, w)
}

val fido = Dog("Fido", 11)
println( fido )
val fido2 = fido.updateWeight(12)
println( fido2 )
Run Code Online (Sandbox Code Playgroud)

当我运行代码时,我得到这个输出:

$ scala animal.scala 
Dog(Fido,11)
Dog(Dog: Fido,12)
Run Code Online (Sandbox Code Playgroud)

我想后返回的实际类型有关动物的updateNameupdateWeight已被调用(即不是Animal).我知道,如果我重写updateNameupdateWeight直接,然后正确的类型将被退回,我没有使用抽象类型SubAnimal.

对于抽象类型的值与子类相同的特殊情况,是否有一些棘手的方法来转义抽象类型?

(这被称为"MyType"问题).

Lan*_*dei 9

这应该工作:

trait Animal[T] {
  self:T =>

  val name: String
  val weight: Int

  def updateName(n: String): T = returnMe(n, this.weight)
  def updateWeight(w: Int): T = returnMe(this.name, w)
  // Abstract protected method
  protected def returnMe(n: String, w: Int): T
}

case class Dog(name: String, weight: Int) extends Animal[Dog] {
  override def returnMe(n: String, w: Int): Dog = Dog("Dog: " + name, w)
}

case class Cat(name: String, weight: Int) extends Animal[Cat] {
   override def returnMe(n: String, w: Int): Cat = Cat("Cat: " + name, w)
}
Run Code Online (Sandbox Code Playgroud)

类似的东西case class Cat(name: String, weight: Int) extends Animal[Dog]会被编译器拒绝.代码被盗改编自http://oldfashionedsoftware.com/2009/12/10/self-help/


one*_*one 6

使用类型参数化?

trait Animal[A <: Animal[A]] {
  val name: String
  val weight: Int

  def updateName(n: String) = returnMe(n, this.weight)
  def updateWeight(w: Int) = returnMe(this.name, w)
  // Abstract protected method
  protected def returnMe(n: String, w: Int): A
}

case class Dog(name: String, weight: Int) extends Animal[Dog] {
  override def returnMe(n: String, w: Int) = Dog("Dog: " + name, w)
}

case class Cat(name: String, weight: Int) extends Animal[Cat] {
  override def returnMe(n: String, w: Int) = Cat("Cat: " + name, w)
}
Run Code Online (Sandbox Code Playgroud)

  • 抽象类型不仅仅是语法糖.虽然您经常可以将抽象类型版本转换为通用版本,反之亦然,但它们并不等效:如果您有Foo [X],则每个实例化(如Foo [Int])都会建立新类型.如果你有Foo {type X},那么只有一个Foo类型,恰好包含一个抽象类型变量. (2认同)

Ale*_*ise 6

最近关于这个主题的一些讨论 ......你正在寻找的东西通常被称为"MyType",它的典型Scala/Java编码使用递归类型参数:

public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable { 
    public final int compareTo(E o) 
    // ... 
}
Run Code Online (Sandbox Code Playgroud)