Continuiamo la nostra esplorazione di Scala, questo affascinante linguaggio Object Oriented con retrogusto funzionale. Oggi parliamo dei Tratti: come abbiamo già visto nel Post Introduttivo su Scala 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:
[code lang=”scala”]
abstract class EssereVivente
abstract class Pianta extends EssereVivente
abstract class Animale extends EssereVivente
abstract class Fungo extends EssereVivente
[/code]
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:
[code lang=”scala”]
trait HaGambe extends Animale {
def cammina() { println("Sto camminando!") }
}
[/code]
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:
[code lang=”scala”]
trait HaAli extends Animale {
def muoviAli() { println("Flap Flap…") }
}
[/code]
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:
[code lang=”scala”]
trait PuoVolare {
this: HaAli =>
def vola() { println("Sto volando!") }
}
[/code]
Tutti gli oggetti di tipo Uccello
hanno gambe e ali:
[code lang=”scala”]
abstract class Uccello extends Animale with HaGambe with HaAli
[/code]
ma l’Oca
sa volare, mentre lo Struzzo
no:
[code lang=”scala”]
class Struzzo extends Uccello
class Oca extends Uccello with PuoVolare
[/code]
Creiamo infine un bel Gatto
:
[code lang=”scala”]
class Gatto extends Animale with HaGambe
[/code]
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:
[code lang=”scala”]
object TrattiMain {
def main(args: Array[String]): Unit = {
val gustavo = new Oca()
gustavo.vola
}
}
[/code]
Ma non solo: possiamo arricchire di tratti una variabile mentre la istanziamo: in questo modo uno struzzo particolarmente atletico può riuscire a volare:
[code lang=”scala”]
val struzzoAtleta = new Struzzo() with PuoVolare
struzzoAtleta.vola
[/code]
Che bel trucchetto, vero? Adesso voi direte: facciamo volare anche un gatto!
[code lang=”scala”]
val gattoVolante = new Gatto() with PuoVolare
gattoVolante.vola
[/code]
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:
[code lang=”scala”]
val gattoVolante = new Gatto() with HaAli with PuoVolare
gattoVolante.vola
[/code]
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!
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