Close

Not a member yet? Register now and get started.

lock and key

Sign in to your account.

Account Login

Forgot your password?

Alla faccia dell’interfaccia: Scala a Tratti

02 Aug '11 by in Java, Scala | 2 comments


  • Sharebar



ScalaContinuiamo 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

Uml Esseri ViventiSupponiamo 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:


Uml Esseri Viventi Completo

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!

  • Alessandrocolonna10

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

    • http://twitter.com/manupia Manuele Piastra

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