Различные нотации для выражения наследования - PullRequest
5 голосов
/ 26 ноября 2010

Почему существуют разные обозначения для выражения наследования?В дженериках я должен использовать оператор <: - - в обычном наследовании классов я должен использовать ключевое слово extends. </p>

Например, я должен написать это:

class X[A <: B] extends Y

Нопочему бы не написать что-то вроде этого:

class X[A <: B] <: Y

или

class X[A extends B] extends Y // like Java

У меня нет проблем с текущей нотацией, но я хочу знать, есть ли причина для обозначения иерархии типовдженериков по-другому.

Ответы [ 2 ]

13 голосов
/ 26 ноября 2010

Что ж, ничто не может остановить Scala от этого, но на самом деле они не выражают одно и то же вообще . И, на самом деле, вы можете видеть это в Java, где вы можете написать X super Y, но вы не можете сказать class X super Y.

Ключевое слово extends выражает связь между классами , наследованием. С другой стороны, <: и >: выражают связь между типами , одной из границ. Когда я говорю X <: Y, то для X и Y допустимо быть, например, String, тогда как String extends String будет бессмысленным. Это также тот случай, когда List[String] <: List[AnyRef], хотя, опять же, List[String] extends List[AnyRef] не имеет смысла. И, просто чтобы подчеркнуть, это не правда, что Set[String] <: Set[AnyRef]. Во всех приведенных мною примерах речь идет об одном и том же классе , но не обязательно об одном и том же типе .

.

И, конечно, существуют другие отношения между типами, такие как границы представления (<%) и границы контекста (:).

Итак, из-за того, что extends подразумевает <:, из этого не следует, что <: подразумевает extends, что само по себе является достаточной причиной, чтобы избегать использования одного и того же ключевого слова. Добавьте к этому другие отношения между типами, и вы получите почти закрытое соглашение.

7 голосов
/ 26 ноября 2010

Это становится немного более очевидным, когда вы расширяете (без каламбура) свой пример за пределы этого простого случая.

Множественное наследование:

class C[A] extends X with Y with Z

Примеси:

val x = new X with Y

параметризация:

class X[A <: B] extends Y[A]

Несколько (связанных) типов параметров:

class X[A >: B, B](x: A, xs: Seq[B])

Контекстные границы:

class X[A : Manifest]

Просмотр границ:

class X[A <% Ordered[A]]

Универсальный Методы :

class Foo[B] {
  def bar[A <: B](x: A) = ...
}

Как вы можете видеть, отношения, которые могут быть указаны в параметре типа, намного богаче, чем простая линейная иерархия, доступная при объявлении класса, особенно когда вы допускаете границы.

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

val myList = List(1, 2, 3)

вместо

val myList = List[Int](1, 2, 3)

Таким образом, способы использования обозначений сильно отличаются.

обновление

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

def someMethod[T <: Foo with Bar](x: T) = ...

Это требует, чтобы type-param T был подтипом, смешивающим как Foo, так и Bar.

То же самое относится к типам конструкций:

type Closable = { def close: Unit } //alias for a structural type
def someMethod[T <: Closable](x: T) = ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...