Da Java a Scala
Ormai da anni programmo in Java e devo dire che è un linguaggio che, dopo le prime fasi di difficoltà e frustrazione, sa dare tante soddisfazioni. E’ vasto, potente, offre maree di librerie, usato bene è come la bacchetta di Harry Potter. Però diciamocelo: anche i più appassionati Javisti non potranno negare che a volte ci si trova a gestire alcune situazioni in modo bizzarro, altre in modo macchinoso o complesso.
Per questo motivo periodicamente mi guardo intorno spinto dalla domanda: c’è qualcosa di nuovo? Infatti bisogna tener presente che Java compirà vent’anni il prossimo anno e nel frattempo si è scoperto che il paradigma di programmazione orientato agli oggetti non è la panacea che si credeva.
Caccia al tesoro
In questi ultimi mesi ho trovato due linguaggi che hanno attirato la mia attenzione: Groovy e Scala. Entrambi presentano caratteristiche molto interessanti, tra cui:
- Estendono Java, arricchendola di costrutti aggiuntivi;
- Aggiungono aspetti funzionali a quelli object oriented, rendendo il codice necessario per compiere alcuni task più coinciso ma espressivo;
- Si integrano in modo trasparente con le classi e le librerie Java esistenti;
- Si compilano in bytecode Java, quindi girano sulla JVM e possono essere usati ovunque si usa Java;
- Possiedono plugin per i più diffusi IDE (Eclipse, Netbeans e Idea IntelliJ).
Salendo la Scala per mano a Java
Alla fine ho deciso di approfondire il linguaggio Scala, per il semplice motivo che Scala, a differenza di Groovy, rimane comunque un linguaggio strongly typed e per me, che sono un tipo da mutuo a tasso fisso, la cosa è molto rassicurante. Passo quindi a condividere con i miei venticinque lettori quello che ho imparato finora.
Cos’ è Scala
Per chi è un Javista (ce n’è uno anche nelle migliori famiglie) Scala possiede una sintassi molto simile e concetti analoghi, con qualche differenza importante. Scala è:
- object oriented: ogni cosa è un oggetto. I tipi e i comportamenti degli oggetti sono descritti da classi e tratti. I tratti sono l’analogo delle interfacce Java, ma possono avere anche dei metodi implementati, agevolando il riutilizzo del codice
- funzionale: in Java non esistono funzioni che non siano incluse in un oggetto, ovvero che non siano metodi, Scala consente di creare funzioni vere e proprie e di utilizzare le closure
- strongly typed: come Java questo linguaggio costringe il programmatore a utilizzare variabili fortemente tipate, in modo da fornire una alta probabilità che molti errori saranno individuati in fase di compilazione e non a runtime. Questo non deve implicare rigidità (come in Java sono presenti i Generics) nè dover scrivere codice inutile: in Scala esiste un sofisticato sistema di local type inference che consente allo sviluppatore di non specificare il tipo di variabili e parametri quando il compilatore può evincerli da soli.
Queste caratteristiche danno vita ad un linguaggio elegante ma potente, sicuro ma flessibile, chiaro ma coinciso e che può integrarsi perfettamente nel codice Java preesistente. Molti dicono che Scala combina il meglio di Java e Ruby (il linguaggio di programmazione, eh!): insomma, viene davvero voglia di provarlo, no?
Da dove parto
Cominciare a programmare Scala è facile: basta scaricarlo dal sito ufficiale. Ovviamente andremo a scrivere codice Scala da dentro un IDE e in questo caso abbiamo l’imbarazzo della scelta: Netbeans, Eclipse o IntelliJ! Le istruzioni per installare il plugin nelle sue diverse versioni sono disponibili alla pagina IDE and Editor plugins.
Vediamo un po’ di codice, dai!
HelloWorld! Singleton
Come da antica tradizione partiamo con un HelloWorld!:
object HelloWorld { def main(args: Array[String]) { println("Hello, world!") } }
Beh, ci sarebbero già tante cose da dire. Una classe Scala si definisce con class
, ma con object definiamo un singleton! Scala fa piazza pulita di metodi e classi statiche, perché filosoficamente vanno contro il principio di scope ridotto e information hiding dei linguaggi Object Oriented; quindi per realizzare un tipo da usare in singola istanza si usa object
. Notiamo inoltre che:
- il punto e virgola a fine frase non compare più: in Scala diventa opzionale, è necessario solo nel caso in cui vengano inserire più istruzioni nella stessa riga;
- per definire un metodo o una funzione si utilizza la parola chiave
def
.
HelloWorld! da linea di comando
Scala consente anche di lanciare al volo comandi da linea di comando. Se avete installato Scala potete avviare il prompt (su Windows) o un terminale (su Linux) e digitare scala:
A questo punto l’Hello World! diventa facile come scrivere
println("Hello World!")
:Any?
In Java ogni oggetto ne estende un altro: dove non esplicitamente dichiarato stiamo estendendo Object
(ogni cosa è un oggetto!). In Scala è la stessa cosa, ma al vertice della gerarchia di tipi c’è Any
: ne parleremo ancora tra poco.
Via i tipi primitivi
I tipi base di Scala sono gli stessi di Java: Boolean, String, Int, Double
e così via. Sparisce la barocca differenza tra tipi primitivi (quelli minuscoli) e quelli incapsulati (esempio: int
e Integer
) con la necessità di boxing e unboxing. In Scala i tipi base sono anch’essi oggetti: è assolutamente valido scrivere:
object HelloWorld { def main(args : Array[String]) = Console.println("Hello my " + 25.toString() + " readers") }
ovvero il numero 25
ha il toString
perché è in realtà un oggetto!
I tipi base di Scala estendono AnyVal
, a sua volta sottoclasse di Any
. A questo punto forse un bel diagramma con le gerarchie dei tipi di Scala può aiutare:
Al vertice della gerarchia, come dicevamo, c’è Any
. Questo tipo è esteso da due sottotipi: AnyVal
ed AnyRef
, che dividono il mondo dei tipi Scala in due: classi valore e classi riferimento. Le classi valore sono i tipi predefiniti, in Java i tipi primitivi. Tutte le altre classi definiscono tipi riferimento. I tipi riferimento si dividono a loro volta in: classi Scala, tra cui quelle definite dall’utente, che estendono ScalaObject
, e classi del Java runtime environment, che estendono AnyRef
(che quindi va a corrispondere a java.lang.Object
).
Ancora vivi? Bene, adesso ci rinfranchiamo con un po’ di codice!
Una classe d’esempio
Vediamo come si scrive una classe Point
che ha due proprietà, x
ed y
, e due metodi, move
e translate
:
class Point(var x: Int, var y: Int) { def this() = this(0,0) def move(x1:Int, y1:Int) = {x=x1; y=y1} def translate(dx: Int, dy: Int) = { x += dx; y += dy; } override def toString = "("+x+","+y+")" }
La classe inoltre ridefinisce il toString
che eredita da AnyRef
. Notiamo che la firma dei metodi è così costituita:
def nome_metodo(parametri) : tipo_ritorno = { corpo_del_metodo }
ma in realtà Scala è molto furbino e quindi:
- Il tipo di ritorno del metodo può essere omesso se è inferibile: Scala desume che sia il valore dell’espressione dell’ultima riga del corpo del metodo
- Quando il metodo non ritorna nessun valore Scala ritorna
Unit
(che sostitusce ilvoid
di Java) - La funzione viene definita con
=
seguito da blocco di codice (delimitato da{
e}
). Se il corpo della funzione è composto da una sola istruzione le parentesi graffe possono essere omesse
Guarda mamma, senza cicli!
Vi lascio con quest’ultimo snippet di codice: supponiamo di avere una lista che contiene i primi 8 numeri naturali:
// il tipo lo metto solo a destra, metterlo a sinistra è ridondante val l = List( 1, 2, 3, 4, 5, 6, 7, 8 )
val
serve per dichiarare una variabile immutabile: la parola chiave final
non esiste più in Scala. Adesso questa lista voglio filtrarla, lasciando solo i numeri dispari. In Java dovrei:
- istanziare una lista
result
vuota; - ciclare la lista
l
: ogni elementoi
viene incluso inresult
se è dispari; - terminata l’iterazione della lista
l
restituireresult
.
In Scala invece la cosa è molto rapida:
// l2 contiene 1, 3, 5, 7 val l2 = l.filter(x => x % 2 == 1)
La funzione filter
accetta come parametro una funzione; il tipo della funzione (sì, le funzioni sono in realtà un oggetto e quindi hanno un tipo!) è: un parametro di input del tipo contenuto nella lista, un parametro di output di tipo Boolean
. La funzione può essere definita al volo in modo anonimo dentro alle parentesi di filter
: in questo caso se x modulo 2 è uguale ad 1 restituisce true
, altrimenti false
.
Faccio notare che la lista di partenza l
non viene modificata da filter
. Tutte le collection in Scala sono immutabili (come String
in Java). Questo garantisce codice più sicuro in caso di concorrenza e spesso performance maggiori.
Conclusioni
Scala è un linguaggio affascinante: potente e flessibile, che sembra racchiudere il meglio di diversi linguaggi. In questo post abbiamo solo salito i primi gradini di Scala, ma ovviamente ci sarebbero ancora molte rampe davanti a noi. Spero che avremo modo nei prossimi post di salire ulteriormente assieme.
E voi cosa ne pensate? Serviva un linguaggio come Scala oppure Java bastava? Che impressione vi ha fatto? Pensate di provare un Hello World! in Scala oppure no? Sono curioso di sentire le vostre impressioni!
A presto!
Pingback: ()
Pingback: ()
Pingback: ()