Есть ли тип "SELF" в scala, который представляет текущий тип?

Я изучаю Scala, и есть что-то, что я не могу узнать о языке:

Некоторое время назад мне очень нравилось программировать в Lisaac, а в Lisaac я мог написать класс PERSON с слотом list:ARRAY[SELF], который был эквивалентен list:ARRAY[PERSON], так как SELF - это тип объект, в котором находится этот слот.

Но, используя SELF, если я напишу второй класс STUDENT, который наследует от PERSON, тогда STUDENT наследует изменение слота SELF для STUDENT, поэтому STUDENT будет иметь список STUDENT вместо PERSON.

Можно ли это сделать в Scala? Я ничего не могу узнать об этом.

Спасибо!

Ответы

Ответ 1

Я не уверен, действительно ли это будет полезно для вас, но самое близкое, о чем я могу думать, - this.type. Например:

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

Для этого существует идиома, и она широко используется в рамках коллекций (во всех классах типа Like, например TraverseableLike). Вам необходимо добавить self-type как параметр типа (как это возможно в С++ с 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] 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()

Ответ 3

Ключевое слово this в Scala более или менее эквивалентно.

При разработке расширяемого программного обеспечения иногда удобно явно объявить тип значения this:

Явно напечатанные сообщения о себе в Scala
http://www.scala-lang.org/node/124

Ответ 4

Одиночные типы и ETSR не решают проблему. Я сам искал одну и ту же функцию в Scala, но, по-видимому, ей не хватает так называемых аннотаций самонастройки.

Существуют обстоятельства, при которых такие аннотации типа самостоятельного типа могут быть очень полезными. Рассмотрим пример (адаптирован из Пример вопроса о параметрах типового типа):

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

Все в порядке, и все работает так, как ожидалось. Единственное, что касается того, что потребитель библиотеки должен использовать параметр self-bound type (# 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

Интуитивно, у нас есть несколько вариантов:

  • make 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? К сожалению, я не знаю, как сложно реализовать такую ​​логику, поскольку могут возникнуть проблемы с пресловутой стиранием JVM.

P.P.S. Или, может быть, есть еще одна Scala -уровень, о которой я не знаю?