Publié par

Il y a 11 ans -

Temps de lecture 4 minutes

Traits Scala

Dans les précédents billets consacrés à Scala, nous avons traité du mécanisme d’inférence et des constructeurs et méthodes.
Dans ce billet, nous allons étudier les interfaces en Scala. Celles-ci ont peu de similarités avec les interfaces en Java. Le langage Java ne permet pas de faire de l’héritage multiple, mais les interfaces permettent en quelque sorte de contourner ce problème.

Exemple en Java

Nous allons prendre l’exemple d’une voiture hybride qui peut être soit à essence soit électrique. En Java nous aurions ceci :


public abstract class AbstractCar {
	public abstract String drive();
}

public interface IOilCar {
	public void fill();
}

public interface IElectricalCar {
	public void reload();
}

public class OilCar extends AbstractCar implements IOilCar {

	public String drive() {
		System.out.println("Drive an oil car");
		return null;
	}

	public void fill() {

	}

}

public class ElectricalCar extends AbstractCar implements IElectricalCar {

	public String drive() {
		System.out.println("Drive an electrical car");
		return null;
	}

	public void reload() {

	}

}





public class HybridCar extends AbstractCar implements IElectricalCar, IOilCar {

	public String drive() {
		return null;
	}

	public void reload() {

	}

	public void fill() {

	}

}

Bien que la déclaration soit longue, nous voyons que la classe HybridCar est bien une OilCar et une ElectricalCar. Malheureusement les méthodes reload() et fill() sont implémentées plusieurs fois, ce qui implique de la redondance, de la verbosité et de potentielles erreurs de surcharge et/ou de logique. De plus si nous allons un peu plus loin, il est possible de créer des ElectricalCar qui ne sont pas des voitures… Certes d’un point de vue design cela ne se fait pas, mais rien ne nous l’interdit.

Scala a voulu corriger ce problème et propose une autre approche sous la forme des traits, qui sont en fait une combinaison des interfaces Java et des mixins Ruby.

Exemple en Scala

Ainsi, si nous reprenons notre exemple nous aurions :


abstract class Car {
  def drive() = {
    print("Drive an");
  }
}

trait ElectricalCar extends Car {
  
  override def drive = {
    	super.drive();
	println(" electrical car");    
  }
  
  def reload () = {
    
  }
}

trait OilCar extends Car {

  override def drive = {
        super.drive();
        println(" oil car");    
  }
  
  def fill () = {
    
  }
}

class hybridCar extends OilCar with ElectricalCar {
//...
}

@Override et override

En Java, il est fréquent de vouloir modifier le comportement d’une méthode dans les classes dérivées. L’annotation @Override a été introduite afin de vérifier que les méthodes d’une classe dérivée surchargent bien une méthode de la superclasse, mais celle-ci n’est pas obligatoire. En Scala, override est un mot clé du langage et si celui-ci est omis, une erreur de compilation sera générée. D’où la présence de override pour la méthode drive dans les classes dérivées.

Retour au code

Si nous faisons appel à la méthode drive d’une instance de hybridCar, le compilateur sait que celle-ci fait aussi référence à la méthode drive du trait ElectricalCar (on parle alors de mixin).
Ainsi avec le code suivant :


val hCar = new hybridCar();  
hCar.drive

Nous avons le résultat suivant :

Drive an oil car
electrical car

Bien entendu si nous changeons l’ordre du mixin, nous aurons :

Drive an electrical car
oil car

Dans cet exemple en Scala, la classe hybridCar est bien un sous type de Car, ElectricalCar et OilCar. Les traits en Scala permettent non seulement de définir des méthodes/variables abstraites, mais aussi de définir des méthodes, et peuvent aussi étendre d’autres classes ou traits. La force des traits réside dans le fait que le compilateur va les traiter comme des classes héritées. Ainsi, les membres non abstraits des traits font en quelque sorte partie des sous-classes.
Nous pouvons dire que les traits Scala sont dans un certain sens un mix des notions d’interface et de classes abstraites Java. Néanmoins, le point noir les concernant est que les traits Scala n’acceptent pas de paramètres dans leurs constructeurs.

Les traits représentent un mécanisme puissant, car ils permettent de définir une interface que le développeur doit utiliser (ie. interface Java), mais aussi de définir des comportements (ie. abstract Java). L’exemple qui a été développé ici ne représente qu’une petite partie des possibilités qu’offrent les traits, et je vous invite à télécharger Scala et à essayer par vous-même les possibilités de ce langage fonctionnel.

Références

Publié par

Publié par Nicolas Jozwiak

Nicolas est delivery manager disposant de 12 ans d’expérience en conception et développement. Son parcours chez un éditeur avant son entrée chez Xebia lui a notamment permis de développer de solides compétences dans le domaine de la qualité et de l’industrialisation (tests, intégration continue, gestion de configuration, contrôle qualité). Bénéficiant d’une expérience très solide de mise en place des méthodes agiles et d’accompagnement d’équipes sur le terrain, il s’attache à mettre à profit quotidiennement son expérience qui est reconnue pour son approche pragmatique, proactive et pédagogique.

Commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Xebian, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.