scala中是否存在表示当前类型的“SELF”类型?

时间:2009-10-19 03:13:32

标签: inheritance scala

我正在学习Scala,有一件事我无法找到该语言:

前段时间我在Lisaac编程非常舒服,在Lisaac中,我可以用PERSON编写一个list:ARRAY[SELF]课,相当于list:ARRAY[PERSON]SELFSELF 1}}是该插槽所在的对象的类型。

但是使用STUDENT,如果我写的是继承自PERSON的第二个类STUDENT,那么SELF会继承STUDENT的{​​{1}} {1}},因此STUDENT会有一个STUDENT而非PERSON的列表。

可以在Scala中完成吗?我无法找到任何相关信息。

谢谢!

4 个答案:

答案 0 :(得分:15)

这有一个习惯用法,它在集合框架中广泛使用(在所有* Like类中,例如TraversableLike)。 您需要将self-type添加为超类的类型参数(就像在C ++中使用CRTP一样):

trait Person[+Self] {
  this: Self => //Declare that any concrete subclass must implement Self; therefore, this can be used with type Self.
  //val list: Array[Self] //Not sure that this will work so easily, for the same reason new T[] does not work in Java.
  val list = Seq[Self]() //No problem here; Array is really special.
}

在定义这个类之后,我们可以尝试在解释器中定义子类:

scala> class Student extends Person[Student]
defined class Student
scala> (new Student).list
res0: Seq[Student] = List() //Note the result type
scala> class Student2 extends Person[Student] //Note the mistake
<console>:9: error: illegal inheritance;
 self-type Student2 does not conform to Person[Student]'s selftype Person[Student] with Student
       class Student2 extends Person[Student]

没有阻止的错误就是有了这个定义,其中Self没有被重新定义:

scala> class SpecStudent extends Student
defined class SpecStudent

感谢+前面的Self,这使得它成为一个协变类型参数(我不解释它是什么),但这至少是可能的:

scala> class SpecStudentCorrect extends Student with Person[SpecStudentCorrect]

scala> (new SpecStudentCorrect).list
(new SpecStudentCorrect).list
res1: Seq[SpecStudentCorrect] = List()

答案 1 :(得分:8)

我不确定这对你是否真的有用,但我能想到的最接近的是this.type。 E.g:

scala> class A { val l: List[this.type] = Nil }  
defined class A

scala> new A().l
res3: List[A] = List()

scala> class B extends A
defined class B

scala> new B().l
res4: List[B] = List()

答案 2 :(得分:2)

Scala中的this关键字或多或少等效。

在开发可扩展软件时,明确声明值this的类型有时很方便:

Scala中明确键入的自引用
http://www.scala-lang.org/node/124

答案 3 :(得分:2)

单例类型和ETSR不能解决问题。我自己一直在寻找Scala中的相同功能,但显然它缺少所谓的自我类型注释。

在某些情况下,这种自我类型的注释可能非常有用。考虑一个例子(改编自Circular type parameters question example):

// we want a container that can store elements
trait Container[E <: Element[E]] {
  def elements: Seq[E]
  def add(elem: E): Unit
}

// we want elements be aware of their enclosing container
trait Element[E <: Element[E]] {
  def container: Container[E]
}

假设您将其放入库中。库消费者应该执行以下操作:

object PersonContainer extends Container[Person] {
  // actual implementation is not important
  def elements = Nil
  def add(p: Person) = {}
}

class Person extends Element[Person] {             // {1}
  def container = PersonContainer
}

没关系,一切都按预期工作。唯一关心的是库消费者假设使用自绑定类型参数(代码中的#1)。但那还不是全部。现在假设您考虑到某种ActiveRecord模式,并且您希望将方法save添加到Element,它只是委托给它的容器的add方法。令人惊讶的是,这并不容易:

trait Element[E <: Element[E]] {
  def container: Container[E]
  def save() = container.add(this)   // won't compile
}

found   : Element[E]
required: E

直观地说,我们在这里有几个选择:

  • 使add方法接受Element[E]而不是E;
  • this投放到Element[E]

这些选项都不令人满意,仅仅是因为EElement[E]不同(实现不强制使用自绑定类型参数)。我看到解决这个问题的唯一方法是在Scala中使用自我类型的概念(让我们假设我们用我们喜欢的语言):

trait Container[E <: Element] {
  def elements: Seq[E]
  def add(elem: E): Unit
}

trait Element {  // the type parameter would be redundant ...
  def save() = container.add(this)  // ... and this would be possible, too, ...
  def container: Container[this]  // ... if only we could do this
}

如果编译器可以处理this(或者可能是另一个关键字),当它在方括号内使用时,作为实际实现的类型(即与obj.getClass的结果相同的类型)那么问题就会消失。

P.S。有人可能会考虑将这些东西包含在Scala wishlist中吗?不幸的是,我不知道,实现这样的逻辑是多么困难,因为臭名昭着的JVM擦除可能存在问题。

P.P.S。或者也许还有一些我不知道的Scala方式?