Commençons avec le cyclique de la dépendance.
trait A {
selfA: B =>
def fa: Int }
trait B {
selfB: A =>
def fb: String }
Toutefois, la modularité de cette solution n'est pas aussi grand qu'il peut sembler à première vue, parce que vous pouvez remplacer l'autonomie des types comme ça:
trait A1 extends A {
selfA1: B =>
override def fb = "B's String" }
trait B1 extends B {
selfB1: A =>
override def fa = "A's String" }
val myObj = new A1 with B1
Bien que, si vous remplacer un membre d'une auto de type, vous perdez l'accès au membre d'origine, qui peut encore être accessibles par le biais de super en utilisant l'héritage. Donc ce qui est vraiment acquise au cours de l'utilisation de l'héritage est:
trait AB {
def fa: String
def fb: String }
trait A1 extends AB
{ override def fa = "A's String" }
trait B1 extends AB
{ override def fb = "B's String" }
val myObj = new A1 with B1
Maintenant, je ne peux pas prétendre le comprendre toutes les subtilités du modèle de gâteau, mais il me semble que la principale méthode de l'application de la modularité est au travers de la composition plutôt que de l'héritage ou de l'auto types.
L'héritage de la version est plus courte, mais la principale raison, je préfère l'héritage sur l'auto types est que je trouve qu'il est beaucoup plus difficile à obtenir l'initialisation de l'ordre correct avec auto types. Cependant, il ya certaines choses que vous pouvez faire avec l'auto types que vous ne pouvez pas faire avec l'héritage. Auto types peuvent utiliser un type pendant que l'héritage exige un trait ou d'une classe, comme dans:
trait Outer
{ type T1 }
trait S1
{ selfS1: Outer#T1 => } //Not possible with inheritance.
Vous pouvez même le faire:
trait TypeBuster
{ this: Int with String => }
Bien que vous ne serez jamais en mesure de l'instancier. Je ne vois pas de raison absolue pour ne pas être en mesure d'hériter d'un type, mais je pense qu'il serait utile de disposer de chemin constructeur des classes et des traits que nous avons constructeur de type traits / classes. Comme malheureusement
trait InnerA extends Outer#Inner //Doesn't compile
Nous avons ceci:
trait Outer
{ trait Inner }
trait OuterA extends Outer
{ trait InnerA extends Inner }
trait OuterB extends Outer
{ trait InnerB extends Inner }
trait OuterFinal extends OuterA with OuterB
{ val myV = new InnerA with InnerB }
Ou ceci:
trait Outer
{ trait Inner }
trait InnerA
{this: Outer#Inner =>}
trait InnerB
{this: Outer#Inner =>}
trait OuterFinal extends Outer
{ val myVal = new InnerA with InnerB with Inner }
Un point qui devrait être empathised plus, c'est que les traits peuvent s'étend classes. Merci à David Maclver pour le rappeler. Voici un exemple de mon code:
class ScnBase extends Frame
abstract class ScnVista[GT <: GeomBase[_ <: TypesD]](geomRI: GT) extends ScnBase with DescripHolder[GT] )
{ val geomR = geomRI }
trait EditScn[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]
trait ScnVistaCyl[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]
ScnBase
hérite de la Balançoire Cadre de la classe, de sorte qu'il pourrait être utilisé comme une auto de type et ensuite mélangée à la fin (à l'instanciation). Toutefois, val geomR
doit être initialisée avant d'être utilisée par héritant des traits. Nous avons donc besoin d'une classe à appliquer avant l'initialisation de geomR
. La classe ScnVista
peut alors être hérité de par de multiples orthogonale de traits qui peuvent eux-mêmes être héritée de. À l'aide de plusieurs paramètres de type (génériques) propose une autre forme de modularité.