web-dev-qa-db-fra.com

Mélanger plusieurs traits dans Scala

Note rapide: Exemples du tutoriel Scala pour Java Réfugiés Partie 5: Traits et types.

Supposons que je possède les traits Étudiant, Travailleur, Sous-payé et Jeune.

Comment pourrais-je déclarer une classe ( pas une instance ), CollegeStudent, avec tous ces traits?

Remarque: Je connais les cas les plus simples, comme CollegeStudent avec un ou deux traits:

class CollegeStudent extends Student with Worker
50
Daniel Ribeiro

C'est facile, lorsque vous déclarez une classe, vous utilisez simplement le mot clé "with" aussi souvent que vous le souhaitez

class CollegeStudent extends Student with Worker with Underpaid with Young

l'ordre des traits peut être important si un trait change le comportement de la classe, tout dépend des traits que vous utilisez.

De plus, si vous ne voulez pas avoir une classe qui utilise toujours les mêmes traits, vous pouvez les utiliser plus tard:

class CollegeStudent extends Student
new CollegeStudent with Worker with Underpaid with NotSoYoungAnymore
98
Alexander Stolz

Je pense qu'il est très important d'expliquer non seulement la syntaxe, mais également quel rôle joue l'ordre des traits . J'ai trouvé l'explication dans Jason Swartz Learning Scala (page 177) assez éclairante.

  • Une classe Scala peut étendre plusieurs traits à la fois, mais les classes JVM ne peuvent étendre qu'une seule classe parent. Le compilateur Scala résout ce problème en créant " des copies de chaque trait pour former une haute hiérarchie à colonne unique de la classe et des traits ", un processus connu sous le nom de linéarisation .

  • Dans ce contexte, l'extension de plusieurs traits avec des noms de champs identiques ne pourrait pas être compilée, exactement la même " comme si vous étendez une classe et fournissez votre propre version d'une méthode mais ne parvenez pas à ajouter un mot-clé de remplacement = ".

Et comme il détermine la forme de l'arbre d'héritage, l'ordre de linéarisation est en effet une question très importante à considérer. Par exemple, class D extends A with B with C (Où A est une classe et B et C sont des traits) deviendrait class D extends C extends B extends A. Les quelques lignes suivantes, également extraites du livre, illustrent parfaitement cela:

trait Base { override def toString = "Base" }
class A extends Base { override def toString = "A->" + super.toString }
trait B extends Base { override def toString = "B->" + super.toString }
trait C extends Base { override def toString = "C->" + super.toString }
class D extends A with B with C { override def toString = "D->" + super.toString }

Un appel à new D() aurait le REPL imprimer ce qui suit:

 D->C->B->A->Base

Ce qui reflète parfaitement la structure du graphe d'héritage linéarisé.

10
fr_andres