2009-10-19 4 views
13

Я учусь Scala, и есть то, что я не могу узнать о языке:Есть ли тип «SELF» в scala, который представляет текущий тип?

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

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

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

Спасибо!

ответ

8

Я не уверен, если это будет на самом деле быть полезным для вас, но самое близкое, о чем я могу думать, это 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() 
+9

Я не думаю, что «this.type» - это то, что здесь называется. «this.type» - одноэлементный тип; это тип только этого экземпляра. Попробуйте, как вы можете, действительный элемент «List [this.type]» может содержать «this». –

+0

Да, вы правы. Я пытался точно запомнить, как это работает. – Lachlan

2

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

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

Явных типизированные Самостоятельно Ссылки в Scala
http://www.scala-lang.org/node/124

+0

Параметр 'this' ключевое слово не обозначает тип. – Blaisorblade

+0

Никто этого не говорил. –

+0

> Это ключевое слово в Scala более или менее эквивалентно. О чем Вы бы описали? Если это не эквивалентно SELF Lisaac (это тип), то что вы имели в виду? OP запрашивает способ обозначить тип, так как он хочет написать что-то вроде «Array [Self]». И для этого существует общепринятая модель, даже если она имеет ограничения (см. Мой ответ). – Blaisorblade

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 
} 

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

Наглядно, у нас есть несколько вариантов здесь:

  • сделать 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-способ, о котором я не знаю?

+0

Вы должны самостоятельно аннотация типа в признаке элемента: 'черта Элемент [E <: Элемент [E]] { это: E => ... }' После того, как вы едите, что вы можете выйти из связанный с типом. – Blaisorblade

14

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

ошибка, которая не предотвращено оказывает такое определение, в котором Самость не переопределены:

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() 
+0

Вы уверены, что этот более чем двухлетний вопрос не соответствует адекватному ответу? –

+1

Если вы прочтете комментарии к этому ответу, вы согласитесь со мной. – Blaisorblade

+1

Кроме того, почему вы предполагаете, что я не проверял, даже не прочитав принятый ответ?Теперь ссылка, которую вы предоставили, действительно обеспечивает лучший ответ, чем принятый, но я нашел текст вашего ответа запутанным. Тем не менее, решение, которое я дал, является наиболее часто используемым, также потому, что оно лучше, чем тот, который описан в этой ссылке: вы не можете добавить аннотацию самонастройки типа 'this: Type =>' когда 'Type' абстрактный введите член_ того же класса_, но только если он находится во внешнем классе. – Blaisorblade

Смежные вопросы