Il devient un peu plus évident lorsque vous étendez (sans jeu de mots) votre exemple au-delà de ce cas simple.
héritage multiple:
class C[A] extends X with Y with Z
Mixins:
val x = new X with Y
paramétrisation:
class X[A <: B] extends Y[A]
multiples (liés) Type params:
class X[A >: B, B](x: A, xs: Seq[B])
limites Contexte:
class X[A : Manifest]
Bounds Voir:
class X[A <% Ordered[A]]
Générique Méthodes:
class Foo[B] {
def bar[A <: B](x: A) = ...
}
Comme vous pouvez le voir, les relations qui peuvent être spécifiées dans un paramètre de type sont beaucoup plus riches que la simple hiérarchie linéaire disponible lors de la déclaration d'une classe, surtout quand vous faible pour les limites.
Il est également intéressant de noter que les paramètres de type générique pour les classes ou les méthodes seront inférées très souvent, ce qui vous permet d'écrire:
val myList = List(1, 2, 3)
au lieu de
val myList = List[Int](1, 2, 3)
Ainsi, la manière dont les notations sont utilisés est très différent.
mise à jour
Un exemple particulier a le printemps vient à l'esprit, ce qui démontre l'utilisation simultanée des deux notations et montrant comment ils doivent rester distincts:
def someMethod[T <: Foo with Bar](x: T) = ...
Cela nécessite que le type param T
être un sous-type quelque chose mélangeant à la fois Foo
et Bar
.
de même avec les types de structure:
type Closable = { def close: Unit } //alias for a structural type
def someMethod[T <: Closable](x: T) = ...
++ 1 pour parler de la différence entre les classes et les types –
en ce qui concerne les classes et les types en tant que choses différentes, il est logique d'utiliser des notations différentes. Merci pour l'explication. – sschaef