In Scala le classi e i singleton object (e un altro tipo di definizioni, i trait, che verranno presentati a breve) sono organizzati in package. Come in Java, l’appartenenza di una classe a un package è dichiarata all’inizio del file sorgente (l’unità di compilazione) che la contiene mediante la clasuola package
. Ad esempio, un file contenente
package pfun.examples
object Hello {
// ...
}
dichiara che il singleton object Hello
appartiene al package pfun.examples
.
Ogni membro di un package, cioè ogni risorsa (classe, object o trait) definita a un package, ha un nome completo (fully qualified name) formato dal nome del pacakge seguito dal nome semplice della risorsa stessa (quello che viene specificato quando si definisce la risorsa), separati da un punto. Nell’esempio precedente, l’object definito ha un nome semplice Hello
e nome completo pfun.examples.Hello
.
Per convenzione, i file sorgente dei package vengono tipicamente organizzati nel file system come in Java: a ogni package corrisponde una directory, e si trattano i caratteri .
nei nomi dei package come un’astrazione del separatore di directory; ad esempio, il package pfun.examples
corrisponde alla directory pfun/examples
. Tecnicamente, però, Scala permette di organizare i sorgenti in modo arbitrario, indipendentemente dai nomi dei package.
Quando si ha una risorsa definita in un package, ad esempio
package pfun.util
class Rational {
// ...
}
e la si vuole utilizzare in un altro package, è possibile far riferimento a essa per mezzo del suo nome completo,
object Test {
val x = new pfun.util.Rational(1, 2)
}
oppure è possibile importarla e riutilizzarla mediante il suo nome semplice (facendo però attenzione a evitare conflitti tra i nomi importati e/o definiti in un file, che avvengono secondo le stesse regole di Java):
import pfun.util.Rational
object Test {
val x = new Rational(1, 2)
}
L’importazione dei package o dei loro membri avviene tramite la clausola di importazione, import
, che è molto più flessibile rispetto all’analoga clausola esistente in Java.
Le importazioni si possono suddividere in due principali forme:
Un named import elenca esplicitamente i nomi di uno o più membri di un package da importare, usando eventualmente le parentesi graffe per specificare più membri di uno stesso package. Ad esempio, la clausola
import pfun.util.Rational
importa il singolo membro pfun.util.Rational
, mentre la clausola
import pfun.util.{Rational, Other}
importa i membri pfun.util.Rational
e pfun.util.Other
.
Un wildcard import importa tutti i membri di un package. A differenza di Java, che usa l’asterisco (*
), in Scala le wildcard import sono indicate con l’underscore (_
),
import pfun.util._
opzionalmente scritto tra parentesi graffe:
import pfun.util.{_}
Le clasuole di importazione possono occorrere in qualunque punto del codice sorgente, non solo all’inizio del file come in Java. Ciò permette di importare i membri di un package solo nello scope in cui effettivamete servono. Ad esempio:
object Test {
import pfun.util.Rational
// Qui la classe Rational è importata
val x = new Rational(1, 2);
}
// Qui la classe Rational non è importata