Alla faccia dell’interfaccia: Scala a Tratti

Continuiamo la nostra esplorazione di Scala, questo affascinante linguaggio Object Oriented con retrogusto funzionale. Oggi parliamo dei Tratti: come abbiamo già visto nel i tratti sono maledettamente simili alle interfacce in Java; i tratti tuttavia consentono anche di implementare dei metodi (ovvero non devono essere necessariamente super astratti) e possiedono altre caratteristiche interessanti: oggi le vedremo assieme e scopriremo come i tratti di Scala possano essere molto flessibili e potenti.

L’esempio delle cose viventi

Supponiamo di dover modellizzare un modello biologico molto semplice: esiste il concetto più generico di EssereVivente, che a sua volta può essere classificato come Pianta, Animale o Fungo. Questa classificazione è molto generica e le classi del modello corrispondenti sono astratte:

abstract class EssereVivente
abstract class Pianta extends EssereVivente
abstract class Animale extends EssereVivente
abstract class Fungo extends EssereVivente

Tutto sarà un EssereVivente o un suo sottotipo. Supponiamo adesso di voler aumentare il dettaglio: tra questi animali possiamo operare infatti un’ulteriore distinzione, tra quelli che sono dotati di zampe/gambe e quelli che non lo sono. In Java useremmo le interfacce, in Scala sotto con i tratti:

trait HaGambe extends Animale {
  def cammina() { println("Sto camminando!") }
}

Come dicevamo i metodi contenuti nei tratti possono avere un’implementazione (anche se poco significativa come un println :-)). Ma c’è di più in questo caso: cosa significa che il tratto HaGambe estende una classe? Significa che è possibile applicare il tratto solo a classi che sono sottotipi di Animale: in caso contrario il compilatore si lamenterà; bene, siamo riusciti a modelizzare il fatto che solo gli animali possono avere le gambe!

Focalizziamo ora la nostra attenzione sui pennuti: un tratto HaAli ci sta proprio bene:

trait HaAli extends Animale {
  def muoviAli() { println("Flap Flap...") }
}

Vogliamo ora creare un tratto PuoVolare, ma vorremmo che fosse definibile una regola per cui solo chi ha le ali può volare. E’ possibile farlo nel modo seguente:

trait PuoVolare {
  this: HaAli =>
  def vola() { println("Sto volando!") }
}

Tutti gli oggetti di tipo Uccello hanno gambe e ali:

abstract class Uccello extends Animale with HaGambe with HaAli

ma l’Oca sa volare, mentre lo Struzzo no:

class Struzzo extends Uccello

class Oca extends Uccello with PuoVolare

Creiamo infine un bel Gatto:

class Gatto extends Animale with HaGambe

Abbiamo quasi messo in piedi uno zoo, direi che possiamo fermarci! Vediamo con un bel diagramma UML riepilogativo il modello che abbiamo costruito:



A proposito: questo diagramma è stato creato online con yUML, tool interessante che colgo l’occasione di segnalare.

Venghino siori e siore

Vediamo cosa possiamo fare con la gerarchia di classi e tratti che abbiamo impostato. Ovviamente possiamo fare volare un’oca:

object TrattiMain {
  def main(args: Array[String]): Unit = {

    val gustavo = new Oca()
    gustavo.vola
 }
}

Ma non solo: possiamo arricchire di tratti una variabile mentre la istanziamo: in questo modo uno struzzo particolarmente atletico può riuscire a volare:

val struzzoAtleta = new Struzzo() with PuoVolare
struzzoAtleta.vola

Che bel trucchetto, vero? Adesso voi direte: facciamo volare anche un gatto!

val gattoVolante = new Gatto() with PuoVolare
gattoVolante.vola

Otteniamo invece questo errore in fase di compilazione:

illegal inheritance; self-type it.cosenonjaviste.tratti.Gatto with it.cosenonjaviste.tratti.PuoVolare does not conform to it.cosenonjaviste.tratti.PuoVolare‘s selftype it.cosenonjaviste.tratti.PuoVolare with it.cosenonjaviste.tratti.HaAli TrattiMain.scala /Tratti/src/it/cosenonjaviste/tratti line 12 Scala Problem

Ricordate? Avevamo stabilito che il tratto PuoVolare può essere applicato solo a chi possiede anche il tratto HaAli. Possiamo sistemare attaccando al nostro gatto delle ali di carta:

val gattoVolante = new Gatto() with HaAli with PuoVolare
gattoVolante.vola

Conclusioni

Con questo breve post zoologico abbiamo salito un altro gradino di Scala e abbiamo visto come i tratti siano flessibili e sicuri: Platone, che definiva l’essere umano un “Bipede implume dotato di anima“, aveva sicuramente in mente i tratti Scala, nella sua lungimiranza :-).

L’ereditarietà multipla del C++, potentissima ma complicatissima, sembra essere stata sacrificata sull’altare della semplificazione dei linguaggi più moderni mentre l’astrazione delle interfacce offerta da Java talvolta rende difficile centralizzare il codice; per fortuna sembra proprio che la flessibilità sia un…Tratto distintivo di Scala.

Alla prossima!

Manuele Piastra

Sono uno Scrum Master e Project Manager i cui skill tecnici sono focalizzati al momento sullo sviluppo di applicazioni Java EE su IBM Websphere 7.0 utilizzando JSF (RichFaces), JPA (EclipseLink) ed EJB3. Presso OmniaGroup ricopro il ruolo di Training Manager: seleziono il personale tecnico, mi occupo della sua crescita formativa organizzando Corsi e Workshop sia interni che esterni, molti dei quali hanno visto me come docente. -

  • Alessandrocolonna10

    Molto interessante, sei molto bravo nello spiegare!!:D

    • Ciao, grazie mille per i tuoi complimenti. Ho trovato Scala molto, molto interessante: spero di poterla provare al lavoro prima o poi. Saluti