Это становится немного более очевидным, когда вы расширяете (без каламбура) свой пример за пределы этого простого случая.
Множественное наследование:
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) = ...