Les nombres rationnels : premier code Scala

3 niveaux de maîtrise de Scala

Martin Odersky, le créateur de Scala, a définit 3 niveaux de maîtrise du langage. Le 1er niveau peut être vu simplement comme un « meilleur Java », et les idées sont très facile à maîtriser pour un développeur Java. Ci-dessous est créée une classe qui montre quelques concepts simples pour introduire le langage.

REPL

Scala dispose d’une REPL. Cela signifie Read-Eval-Print-Loop, soit en français Lire-Évaluer-Écrire-Recommencer. C’est un outil en ligne de commande qui permet d’exécuter directement du code Scala, sans avoir à passer par la phase de compilation. Cet outil se lance avec la commande scala sans argument. Le code qui suit peut être directement copié dans la REPL.

Les nombres rationnels

Les nombres rationnels sont ceux qui s’écrivent comme un quotient de 2 entiers relatifs, noté a/b. a est nommé le numérateur et b le dénominateur. Par exemple : 1/2, -3/5, 1234/3, ou encore 10/1.

class Rationnel(val numerateur: Int, val denominateur: Int)

Cette unique ligne suffit pour définir la classe, avec les 2 champs, qui sont publics et non modifiables. On peut maintenant écrire

val r = new Rationnel(1, 2)
println("Numérateur = " + r.numerateur)

val définit une valeur immutable, aussi bien dans le constructeur de la classe que lors de la création du rationnel r. Le constructeur par défaut est celui avec les arguments juste après le nom de la classe. Il n’y a pas de point-virgule (;), qui seraient nécessaire en Java, mais sont optionnels en Scala.

Le type de r est déterminé automatiquement par le compilateur, en fonction du type de la partie droite. Cette détection du type par le compilateur est très puissante, et permet de réduire énormément le code à saisir. On aurait pu écrire

val r : Rationnel = new Rationnel(1234, 3)

Le mot clé val définit une valeur immutable. Écrire ensuite r = new Rationnel(10, 1) génère une erreur de compilation. Le mot clé var permet de définir une variable que l’on peut réassigner.

Version améliorée de la classe

Voici une 2e version de la classe.

case class Rationnel(numerateur: Int, denominateur: Int) {
  def this(n: Int) = this(n, 1)
  val reel = numerateur.toFloat / denominateur
  println("valeur réelle = " + reel)
  override def toString = numerateur.toString + " / " + denominateur
}
val negatif = Rationnel(-3, 5)
val dix = new Rationnel(10)
println(dix.toString)

Un constructeur auxiliaire a été définit. Les constructeurs auxiliaires doivent toujours référencer un autre constructeur, et en bout de chaîne il doit y avoir le constructeur par défaut. Le corps du constructeur est le contenu de la classe lui-même. Ici est définit puis affichée la valeur reel, dans le constructeur.

Le mot def définit une fonction ou une méthode.

toString surcharge la méthode sans argument bien connue des classes Java. Les parenthèses pour l’appel de la méthode sont optionnelles. Du coup, lors de son utilisation, il n’est plus possible de faire la différence entre l’accès à un membre, et l’appel d’une méthode sans paramètre. Cette idée se base sur le fait que dans la programmation fonctionnelle, on cherche à n’utiliser que des objets immutables. Donc les méthodes renvoient toujours la même valeur avec les mêmes paramètres d’entrée. Savoir si on utilise une méthode ou directement un membre n’a donc plus d’importance.

Le plus remarquable ici, est l’ajout du mot clé case. Cela définit ce qu’on appelle de manière très originale une case class. Elle a de nombreuses propriétés intéressantes :
– les méthodes equals, hashCode et toString sont définies par défaut, en utilisant les valeurs du constructeurs par défaut.
– lors de la création d’un objet avec le constructeur par défaut, le mot clé new n’est plus nécessaire, grâce à l’utilisation de la fonction apply dans l’objet compagnon (voir ci-dessous).
– les arguments du constructeur sont des val par défaut. Ce mot clé est optionnel.

Objet compagnon

L’objet compagnon d’une classe sert à définir toutes les méthodes static en Java.

Voici comment ajouter une méthode dans l’objet compagnon, pour simplifier aussi la création des entiers, ainsi que la création d’une constante.

case class Rationnel(val numerateur: Int, val denominateur: Int) {
  def this(n: Int) = this(n, 1)
}
object Rationnel {
  def apply(n: Int) = new Rationnel(n, 1)
  val un = Rationnel(1)
}
println(Rationnel.un.toString)

Les méthodes apply qui sont définies dans l’objet compagnon sont particulières, parce qu’elles peuvent être appelées en utilisant simplement le nom de la classe. Ainsi ces 2 lignes sont équivalentes :

val moinsUn = Rationnel(-1)
val moinsUn = Rationnel.apply(-1)

Conclusion

Voilà la présentation de quelques concepts simples, qui font de Scala un langage compact et très lisible, sans même utiliser l’aspect fonctionnel du langage. La classe Rationnel ci-dessus est bien sûr entièrement compatible avec Java.

Laisser un commentaire

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

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>