2010-11-26 4 views
5

Warum gibt es verschiedene Notationen, um die Vererbung auszudrücken? In Generics muss ich den <: -Operator verwenden - in einer normalen Klassenvererbung muss ich das Schlüsselwort extends verwenden.Verschiedene Notationen zum Ausdruck der Vererbung

Zum Beispiel habe ich schreiben:

class X[A <: B] extends Y 

Aber warum nicht so etwas wie dieses schreiben:

class X[A <: B] <: Y 

oder

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

ich kein Problem mit der aktuellen Schreibweise haben , aber ich möchte wissen, ob es einen Grund gibt, die Typhierarchie von Generika anders zu notieren.

Antwort

12

Nun gibt es nichts Scala zu tun, so, aber, wie in der Tat zu stoppen, sie drücken nicht die gleiche Sache bei allen . Und tatsächlich können Sie das in Java sehen, wo Sie X super Y schreiben können, aber Sie können nicht class X super Y sagen.

Das Schlüsselwort extends drücken Sie eine Beziehung zwischen Klassen, eine der Vererbung. Auf der anderen Seite, <: und >: drücken eine Beziehung zwischen Typen, einer der Grenzen. Wenn ich X <: Y sage, dann ist es sowohl für X als auch Y gültig, zum Beispiel String zu sein, während String extends String sinnlos wäre. Es ist auch der Fall, dass List[String] <: List[AnyRef], obwohl wiederum List[String] extends List[AnyRef] bedeutungslos ist. Und nur um den Punkt zu verdeutlichen, ist es nicht wahr, dass Set[String] <: Set[AnyRef]. In all diesen Beispielen, die ich gerade gegeben habe, sprechen wir über die gleiche Klasse, aber nicht notwendigerweise über die gleiche Typ.

Und natürlich gibt es andere Beziehungen zwischen Typen, wie Sichtgrenzen (<%) und Kontextgrenzen (:).

Also, nur weil extends impliziert <:, es folgt nicht, dass <: impliziert extends, das allein ist Grund genug, um mit dem gleichen Schlüsselwort zu vermeiden. Fügen Sie dazu die anderen Beziehungen zwischen Typen hinzu, und Sie haben so ziemlich einen geschlossenen Deal.

+1

++ 1 für die Diskussion über den Unterschied zwischen Klassen und Typen –

+0

in Bezug auf Klassen und Typen als verschiedene Dinge macht es Sinn, verschiedene Notationen zu verwenden. Danke für die Erklärung. – sschaef

7

Es wird ein bisschen mehr offensichtlich, wenn Sie Ihr Beispiel über diesen einen einfachen Fall hinaus erweitern (kein Wortspiel beabsichtigt).

Mehrfachvererbung:

class C[A] extends X with Y with Z 

Mixins:

val x = new X with Y 

Parametrierung:

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

Multiple (bezogen) Typ params:

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

Kontext:

class X[A : Manifest] 

Ansicht Grenzen:

class X[A <% Ordered[A]] 

Generisches Methoden:

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

Wie Sie sehen können, die Beziehungen sehen, die in einem Typ-Parameter angegeben werden können, sind viel reicher als die einfache lineare Hierarchie, die beim Deklarieren einer Klasse verfügbar ist, besonders wenn Sie al sind niedrig für Grenzen.

Es ist auch erwähnenswert, dass generische Typparameter für die Klassen oder Methoden werden sehr häufig geschlossen werden, so dass Sie schreiben:

val myList = List(1, 2, 3) 

statt

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

So ist die Art und Weise, in der die Notationen verwendet werden, ist sehr unterschiedlich.

Update

Ein besonderes Beispiel ist Frühling gerade in dem Sinne, die Verwendung beiden Schreibweisen gleichzeitig demonstrieren und zeigt, wie sie müssen unterscheidbar bleiben:

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

Dies erfordert, dass der Typ-param T ein Subtyp etwas Mischen in beiden Foo und Bar sein.

Das gleiche gilt für Strukturtypen:

type Closable = { def close: Unit } //alias for a structural type 
def someMethod[T <: Closable](x: T) = ... 
Verwandte Themen