Проблема «MyType»: Нужно ли использовать абстрактные типы (или обобщения) в Scala для возврата фактического класса? - PullRequest
9 голосов
/ 18 октября 2010

Я не уверен, есть ли лучший способ сделать это:

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)

Я хочу вернутьфактический тип животного, о котором идет речь, после того, как updateName или updateWeight был вызван (т.е. не Animal).Я знаю, что если я переопределю updateName и updateWeight напрямую, то будет возвращен правильный тип, и мне не придется использовать абстрактный тип SubAnimal.

Есть ли какой-нибудь хитрый способ избежатьабстрактный тип для особого случая, когда значение абстрактного типа совпадает с подклассом ?

(Это называется проблемой «MyType»).

Ответы [ 3 ]

9 голосов
/ 19 октября 2010

Это должно работать:

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/

6 голосов
/ 18 октября 2010

Некоторые недавнее обсуждение по этой теме ... То, что вы ищете, обычно называют «MyType», и типичная кодировка Scala / Java для него использует параметры рекурсивного типа:

public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable { 
    public final int compareTo(E o) 
    // ... 
}
6 голосов
/ 18 октября 2010

Использование параметризации типа?

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)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...