Langage Scala [2ème partie]

Nous avons vu dans la première partie une introduction de scala avec des démos de ses concepts de base.

Ces démos, j'espère, vous ont donné l'envie de découvrir ce langage fort intéressant.

Dans le passé, l'informatique a été révolutionnée avec le concept "tout est objet" et il me semble qu'un
nouveau concept "tout est fonction" va marquer l'informatique de demain.

Scala tente de rapprocher ces deux concepts en donnant de la liberté aux développeurs de faire le choix

Scala impose un typage statique strict mais lève les restrictions imposées par certains concepts java. Nous y reviendrons dans la 3ème partie.

L'attrait de scala est dans le fait que les meilleures choses qui existent dans les langages récents (objet ou fonctionnel) sont désormais à portée de main.

Passons à la pratique...

Le plan de ce billet est comme suit:

- Lecture de fichier dans scala;
- Notion de traits (interface ou classe abstraite)
- Connection JDBC dans scala

Démo 1. Lecture de fichier

Ce bout de code déjà présenté dans la première partie.
Nous avons vu qu'une seule ligne permet de lire le contenu d'un fichier :

  
	fromFile("nom_fichier")

Simple et rapide car pas de checked exception dans scala.
N'oubliez pas de faire l'import de scala.io.Source.fromFile

Si le fichier n'existe pas il peut être judicieux de récupérer l'exception
pour la traiter dans le programme.
Ceci peut se faire, avec java.io.FileNotFoundException, comme suit:

 
	try{
		  fromFile("nom_fichier")
	catch{
	    case e:FileNotFoundException => {
		logger.error(e.getMessage())
		//traitement de reprise a faire...
	    }
      }

Un point intéressant est de constater qu'en scala il y a un seul bloc try-catch.
Avec le mot clé "case" on peut traiter toutes sorte d'exception dans le même bloc catch.

Un billet sera consacré au mot clé "case" prochainement.

Démo 2. Notion de trait

La notion de trait peut être considérée comme proche de l'interface ou une classe abstraite dans java.
Un trait peut contenir de l'implémentation comme dans les classes abstraites java.

Mais encore une fois c'est complètement différent de java.

La notion de trait permet d'implémenter, à un dosage calculé, l'héritage multiple.
Bien évidemment, les inconvénients de l'héritage multiple ne sont pas complètement levés.
Avec la notion de trait, ils peuvent être contournés.

Vous dites que rien de nouveau ici mais si.

Avec les exemples ci-après, nous allons nous rendre compte de l'utilité des traits.

  * Démo 2.1 Exemple simple

Un trait se déclare avec le mot clé "trait" de scala, écrivons ces deux lignes dans un fichier IVoiture.scala.


		
	trait IVoiture {
	   def avancer():Unit
	}

La classe qui étend (extends car pas de implements dans scala) ce trait est nommée Voiture:

 
class Voiture extends IVoiture {
    val MESSAGE="Ma voiture avance..."
    override def avancer()  { 
	println(MESSAGE) 
	()  //renvoi par defaut value() qui siginifie void
    }
}

* Démo 2.2. Exemple avancé (cette partie un peu difficile est optionnelle pour la suite)

C'est un peu verbeux et long comme exemple.


Il est même inutile pour le résultat obtenu mais il permet de s'exercer sur les notions déjà vues et d'illustrer l'intérêt de la notion "trait".

Cet exemple inspiré d'un projet réel qui, en croisant de nombreux référentiels, tente de fiabiliser les données sur les activités des entreprises.

Ici, la donnée d'activité qui correspond au code Ape dans l'INSEE est comparé à un autre code d'activité du référentiel FINESS.

Le but ici est de définir trois traits et de montrer ensuite comment peut-on adapter le comportement des classes.

Le comportement de ces classes peuvent être adaptées en fonction de l'un des traits (polymorhisme sans héritage ni composition).

Je sais que ce n'est pas clair!

Écrivons quelques lignes de code puis lançons l'exécution qui nous permettra d'y revenir avec des explications claires.

# - Premier trait IReferentiel dont voici le contenu:

 
   trait IReferentiel {
	def getCodeActivite(id:String):String	
  }

# - Le second nommé IRefINSEE qui étend le précédent:

trait IRefInsee extends IReferentiel {
  abstract override def getCodeActivite(id:String): String ={
	super.getCodeActivite(id)+ " FINESS: '" + findByIdActivite(id)+""
  }
  private def findByIdActivite(id:String):String ={
   id match{
     case "8610Z" => "101"
     case _ => throw new 
         IllegalArgumentException("idActivite "+id+" not correct ")	
    }
  }
}

# - Le 3ème trait nommé IRefFiness

trait IRefFiness extends IReferentiel {
 abstract override def getCodeActivite(id:String): String = { 
   super.getCodeActivite(id)+" INSEE:'"+findByIdActivite(id)+"" 
 }
 private def findByIdActivite(id:String):String={
   id match{
    case "101" => "8610Z"
    case _ => throw new 
            IllegalArgumentException(" id="+id+" not correct ")	
   }
 }
}

Et une classe "Entreprise" du modèle dont voici le contenu:

 
class Entreprise (nom:String,identifiantActivite:String) 
extends IReferentiel {
  override def getCodeActivite(identifiantActivite:String): String = {
	   " FROM REFERENTIEL "  
}		 
override def toString():String={
   "Entreprise(nom="+nom+" idActivite="+identifiantActivite+
    " codeActivite='"+getCodeActivite(identifiantActivite)+"')"	
}  
}

Enfin, écrivons un object Main pour test:

object MainTraits {
    def main(args:Array[String]):Unit={
      val entrep:Entreprise=new 
               Entreprise("EntrepF1","125") with IRefFiness 
      println(entrep)

      val entrepInsee:Entreprise=new 
           Entreprise("EntrepSirene1","8610Z") with IRefInsee 
       println(entrepInsee)
   }
}	

Vous constatez que l'attribut entrep est déclaré en ajoutant en fin de ligne "with IRefFiness" ou "with IRefInsee".
Ceci déterminera le comportement de l'instance de cette classe.
Voici un aperçu des résultats que vous devriez obtenir en cliquant droit sur "MainTraits" puis Run As "scala application" :

 
Entreprise(nom=EntrepF1 idActivite=125 codeActivite=' FROM REFERENTIEL  INSEE:'8623Z')
Entreprise(nom=EntrepSirene1 idActivite=8610Z codeActivite=' FROM REFERENTIEL  FINESS: '101')

Pour finir, un bout de code scala pour se connecter à une base (ici c'est Informix car je n'en vois pas beaucoup d'exemples sur le net )

import java.sql.{SQLException,DriverManager,Statement,Connection}
import java.sql.ResultSet
object MainJdbc extends App {
 
 @throws(classOf[SQLException])
 def connect:Connection ={
     classOf[com.informix.jdbc.IfxDriver].newInstance
     DriverManager.getConnection(
       "jdbc:informix-sqli://localhost:PORT/BD:informixserver=IFX_SERVER", "U", "P")
 }	
 val con:Connection=connect 
 val stm:Statement= con.createStatement
 val rs=stm.executeQuery ("select count(*) nb from TABLE")
 var nb=0
 if(rs.next) Console.println("Nb="+rs.getInt("nb")
			  
}

Nous introduisons dans cet exemple, quelques notions intéressantes illustrant, entre autres, le concept "tout est fonction".
Vous notez bien qu'une fonction nommée "connect" est affectée à une variable nommée "con".
L'object MainJdbc ne contient pas de méthode "main".

Je vais devoir m'arrêter là, nous détaillerons ces nouveautés au prochain billet où je reviendrai sur les annotations.

A la prochaine fois.

Laisser un commentaire

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

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.