【问题标题】:"MyType" problem: Do I have to use abstract types (or generics) in Scala to return the actual class?“MyType”问题:我必须在 Scala 中使用抽象类型(或泛型)来返回实际的类吗?
【发布时间】:2010-10-18 18:35:12
【问题描述】:

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

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 )

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

$ scala animal.scala 
Dog(Fido,11)
Dog(Dog: Fido,12)

我想在调用updateNameupdateWeight 后返回相关动物的实际类型(即不是Animal)。我知道如果我直接覆盖updateNameupdateWeight,那么会返回正确的类型,我不必使用抽象类型SubAnimal

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

(这被称为“MyType”问题)。

【问题讨论】:

    标签: generics scala


    【解决方案1】:

    这应该可行:

    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)
    }
    

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

    【讨论】:

    • 嗨@Landei,我看不出这与@one-zero-zero-one 提供的解决方案有什么区别。为什么需要 self:T => Part?当我把它拿走时,我会得到相同的输出。
    • @olle:行为与一零零一的解决方案相同,只是更易于阅读。如果没有 self:T 部分,您可以写 case class Cat(name: String, weight: Int) extends Animal[Int],这显然是不允许的。有关详细信息,请参阅我提供的链接。
    【解决方案2】:

    关于这个主题的一些recent discussion...您要查找的通常称为“MyType”,其典型的 Scala/Java 编码使用递归类型参数:

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

    【讨论】:

    • 所以神奇的方法是使用“this.type”(来自您的链接),但由于我返回一个新实例(不是这个),所以这是不可能的。稍后在帖子中,Odersky 为这个问题推荐了抽象类型,所以我想这会结束讨论。谢谢。
    • 是的,this.type 太窄,无法充当 MyType:它是 this 的类型但没有 this 类的其他实例
    【解决方案3】:

    使用类型参数化?

    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)
    }
    

    【讨论】:

    • 嗯..您的解决方案可能是“抽象类型”旨在简化的。在这种情况下,我认为“类型参数化”与“抽象类型”一样坏/好。你同意吗?
    • @olle:我从不理解抽象类型成员的意义,也从不在我自己的代码中使用它们。我认为它们是语言中不必要的冗余。 (Paul P 最近在邮件列表中表达了类似的想法。)
    • 你是对的,抽象类型似乎是纯糖。但是我非常喜欢它们,因为我认为考虑可以在子类中覆盖的“类型字段”比相应的类型参数语法更容易。
    • 抽象类型不仅仅是语法糖。虽然您通常可以将抽象类型版本转换为通用版本,反之亦然,但它们并不等价:如果您有 Foo[X],则每个像 Foo[Int] 这样的实例化都会建立一个新类型。如果你有 Foo{ type X },那么只有一个 Foo 类型,它恰好包含一个抽象类型变量。
    猜你喜欢
    • 1970-01-01
    • 2017-06-07
    • 2013-10-18
    • 1970-01-01
    • 2013-09-13
    • 2018-08-18
    • 2021-12-13
    相关资源
    最近更新 更多