Ich mochte die Antwort von Roland Ewald, da er sie mit einem sehr einfachen Anwendungsfall des Typalias beschrieb und für weitere Einzelheiten ein sehr schönes Tutorial vorstellte. Da in diesem Beitrag jedoch ein anderer Anwendungsfall mit dem Namen Typmitglieder vorgestellt wird , möchte ich den praktischsten Anwendungsfall erwähnen, der mir sehr gut gefallen hat: (Dieser Teil stammt von hier :)
Abstrakter Typ:
type T
T oben besagt, dass dieser Typ, der verwendet werden soll, noch unbekannt ist und abhängig von der konkreten Unterklasse definiert wird. Der beste Weg, um die Programmierkonzepte immer zu verstehen, ist ein Beispiel: Angenommen, Sie haben das folgende Szenario:
Hier wird ein Kompilierungsfehler angezeigt, da die Eat-Methode in den Klassen Cow und Tiger die Eat-Methode in der Klasse Animal nicht überschreibt, da ihre Parametertypen unterschiedlich sind. Es ist Gras in der Klasse Kuh und Fleisch in der Klasse Tiger gegen Futter in der Klasse Tier, was eine Superklasse ist und alle Unterklassen müssen übereinstimmen.
Zurück zur Typabstraktion: Durch das folgende Diagramm und einfaches Hinzufügen einer Typabstraktion können Sie den Typ der Eingabe in der entsprechenden Unterklasse selbst definieren.
Schauen Sie sich nun folgende Codes an:
val cow1: Cow = new Cow
val cow2: Cow = new Cow
cow1 eat new cow1.SuitableFood
cow2 eat new cow1.SuitableFood
val tiger: Tiger = new Tiger
cow1 eat new tiger.SuitableFood // Compiler error
Der Compiler freut sich und wir verbessern unser Design. Wir können unsere Kuh mit Kuh füttern. Geeignete Lebensmittel und Compiler verhindern, dass wir Kuh mit dem für Tiger geeigneten Futter füttern. Aber was ist, wenn wir zwischen dem Typ von cow1 SuitableFood und cow2 SuitabeFood unterscheiden wollen? Mit anderen Worten, es wäre in einigen Szenarien sehr praktisch, wenn der Weg, über den wir zum Typ gelangen (natürlich über ein Objekt), grundsätzlich eine Rolle spielt. Dank der erweiterten Funktionen von Scala ist Folgendes möglich:
Pfadabhängige Typen:
Scala-Objekte können Typen als Mitglieder haben. Die Bedeutung des Typs hängt von dem Pfad ab, über den Sie darauf zugreifen. Der Pfad wird durch den Verweis auf ein Objekt (auch als Instanz einer Klasse bezeichnet) bestimmt. Um dieses Szenario zu implementieren, müssen Sie die Klasse Gras innerhalb der Kuh definieren, dh Kuh ist die äußere Klasse und Gras ist die innere Klasse. Die Struktur wird wie folgt aussehen:
class Cow extends Animal {
class Grass extends Food
type SuitableFood = Grass
override def eat(food: this.SuitableFood): Unit = {}
}
class Tiger extends Animal {
class Meat extends Food
type SuitableFood = Meat
override def eat(food: this.SuitableFood): Unit = {}
}
Wenn Sie nun versuchen, diesen Code zu kompilieren:
1. val cow1: Cow = new Cow
2. val cow2: Cow = new Cow
3. cow1 eat new cow1.SuitableFood
4. cow2 eat new cow1.SuitableFood // compilation error
In Zeile 4 wird ein Fehler angezeigt, da Gras jetzt eine innere Klasse von Kuh ist. Um eine Instanz von Gras zu erstellen, benötigen wir ein Kuhobjekt, und dieses Kuhobjekt bestimmt den Pfad. 2 Kuhobjekte führen also zu 2 verschiedenen Pfaden. In diesem Szenario möchte cow2 nur speziell dafür geschaffene Lebensmittel essen. So:
cow2 eat new cow2.SuitableFood
Jetzt sind alle glücklich :-)