我正在学习Scala,有一件事我无法找到该语言:
前段时间我在Lisaac编程非常舒服,在Lisaac中,我可以用PERSON
编写一个list:ARRAY[SELF]
课,相当于list:ARRAY[PERSON]
,SELF
,SELF
1}}是该插槽所在的对象的类型。
但是使用STUDENT
,如果我写的是继承自PERSON
的第二个类STUDENT
,那么SELF
会继承STUDENT
的{{1}} {1}},因此STUDENT
会有一个STUDENT
而非PERSON
的列表。
可以在Scala中完成吗?我无法找到任何相关信息。
谢谢!
答案 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]
。这些选项都不令人满意,仅仅是因为E
与Element[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方式?