Scala è un linguaggio a oggetti, in cui la principale astrazione sui dati è la nozione di classe. In seguito, il meccanismo classi in Scala verrà illustrato facendo riferimento a un classico esempio: i numeri razionali.
Si vogliono modellare i numeri razionali, definendo un “pacchetto” che fornisca l’aritmetica su tali numeri. In generale, un numero razionale $\frac x y$ è una coppia di numeri interi: il numeratore $x$ e il denominatore $y$. Per modellare $\frac x y$ in modo elegante, è necessario definire una struttura dati che combini numeratore e denominatore e permetta di trattarli come un’unica entità, definendo funzioni sull’intera struttura e non sui due numeri interi che essa contiene.
Rational
Per iniziare, si definisce la classe Rational
, specificando una lista di parametri formali chiamati parametri di classe (ciascuno dei quali è caratterizzato, come al solito, da un nome e un tipo); in questo caso, i parametri di classe sono il numeratore x
e il denominatore y
, entrambi di tipo int*:*
class Rational(x: Int, y: Int)
Questo è già un frammento di codice completo: siccome (per ora) la classe non contiene codice (ha solo i comportamenti ereditati dalla superclasse di default), si può evitare di indicare le parentesi graffe che racchiuderebbero il corpo (vuoto).
La definizione appena mostrata introduce due elementi:
Rational
Rational
, che permette di creare elementi del tipo Rational
, e prende come argomenti i parametri di classe.Il valore di un oggetto è determinato dai valori dei parametri di classe passati come argomenti al costruttore primario. Si vedrà più avanti che ciò consente l’uso delle classi nel modello di sostituzione.
toString
Un primo metodo che è opportuno definire, o meglio ridefinire, è il metodo toString
che tutte le classi ereditano da java.lang.Object
(si può immaginare che Object
sia la superclasse di tutto, anche se la gerarchia delle classi Scala è più complessa). Esso viene utilizzato ogni volta che è necessario produrre una stringa per descrivere un oggetto, in particolare, è il metodo usato per descrivere il risultato della valutazione di un’esprssione nell’interprete.
Non avendo esplicitamente definito un metodo toString
, la classe Rational
eredita appunto quello di Object
, che restituisce stringhe del tipo Rational@653a0c41
. Se si vuole ottenere una rappresentazione più utile, bisogna sovrascrivere il metodo toString
:
class Rational(x: Int, y: Int) {
override def toString(): String =
if (y == 1) x.toString() else x + "/" + y
}
Su questo codice possono essere fatte varie osservazioni:
def
.override
, altrimenti si ha un errore in compilazione. In questo modo, il compilatore aiuta il programmatore a non sovrascrivere accidentalmente un metodo esistente quando ne vuole invece definire uno nuovo, e viceversa.x.toString()
, dove x
è di tipo Int
, perchè in Scala i valori di tutti i tipi, compresi i tipi base, sono oggetti sui quali è definito il metodo toString
— a differenza di Java, non c’è la distinzione tra tipi primitivi e tipi riferimento (almeno al livello della macchina astratta)else
non è invece necessario effettuare esplicitamente le conversioni x.toString()
e y.toString()
, perchè queste vengono fatte implicitamente dall’operatore di concatenazione di stringhe (+
), secondo le stesse regole che valgono in Java (se almeno un operando di +
è una stringa, allora anche l’altro operando viene convertito in una stringa).