CodeGym /Corsi /JAVA 25 SELF /Introduzione ai moduli: a cosa servono

Introduzione ai moduli: a cosa servono

JAVA 25 SELF
Livello 60 , Lezione 0
Disponibile

1. Problemi del vecchio approccio

“Classpath party”: quando è tutto nella stessa cucina

Prima dell’arrivo dei moduli in Java (nella versione 9) tutto il codice dell’applicazione, le librerie e le dipendenze finivano in un unico grande calderone — il classpath. Di fatto non c’erano confini tra le librerie: qualsiasi classe presente nel classpath poteva essere trovata e usata da chiunque.

Si verificavano conflitti di nomi: due librerie contengono una classe con lo stesso nome completamente qualificato, per esempio com.example.Util — ne veniva scelta “una qualsiasi”, e del problema ci si accorgeva solo da comportamenti strani a runtime.

Classico del genere — dependency hell: una libreria richiede un logger versione 1.2, un’altra la 1.3, e nel classpath finiscono entrambe. La JVM non sa quale prendere e ci si ritrova con un misterioso NoSuchMethodError.

E inoltre: anche se una classe è dichiarata come public ma destinata a usi interni della libreria, altro codice può comunque usarla — e rompersi qualcosa nel processo. I progetti grandi diventavano un campo minato, e la manutenzione — un’avventura.

2. Che cos’è un modulo in Java?

Modulo: come una scatola con i fori

In Java 9 è apparso il sistema modulare (JPMS). Un modulo è una scatola con classi e pacchetti, nella quale fai tu le “finestrine”: indichi esplicitamente cosa esponi all’esterno (exports) e cosa nascondi all’interno. E sì: anche un public-classe non è visibile ad altri moduli se il suo pacchetto non è esportato.

Definizione formale

Un modulo è un’unità logica di suddivisione del codice che raggruppa pacchetti e classi correlati. Ogni modulo dichiara esplicitamente:

  • cosa esporta — ciò che rende disponibile ad altri moduli (exports);
  • e cosa importa — da quali altri moduli dipende (requires).

Proprietà chiave del modulo:

  • Confini espliciti. È definito chiaramente cosa è accessibile dall’esterno e cosa no.
  • Dipendenze esplicite. Un modulo non “vede” un altro modulo senza un esplicito requires.
  • Isolamento. I dettagli interni possono essere completamente nascosti al mondo esterno.

3. Vantaggi della modularità

Incapsulamento migliorato

È apparso un nuovo livello di visibilità — a livello di modulo. Anche se una classe è public, ma il suo pacchetto non è esportato, è visibile solo all’interno del modulo. Le parti interne della libreria non “tracimano” più all’esterno.

Dichiarazione esplicita delle dipendenze

Le dipendenze necessarie vengono elencate in module-info.java. Compilatore e IDE ti avviseranno in anticipo se ti sei dimenticato di indicare il modulo da cui dipendi.

Sicurezza e affidabilità migliorate

Limitare l’accesso alle API interne rende più difficile un intervento accidentale o intenzionale. È fondamentale per le grandi librerie e per i moduli della piattaforma.

Possibilità di creare JRE “minimali”

Con jlink si può assemblare un runtime minimo composto solo dai moduli necessari. Nel cloud e in scenari embedded questo fa risparmiare spazio su disco e memoria.

Bonus: avvio più rapido e dimensioni ridotte

Non vengono caricati tutti i moduli, ma solo quelli realmente utilizzati — le applicazioni si avviano più in fretta e consumano meno risorse.

4. Dove si usano i moduli

Nella libreria standard di Java

A partire da Java 9, la piattaforma stessa è modulare. Esempi:

  • java.base — modulo di base (obbligatorio per qualsiasi programma).
  • java.sql — lavoro con i database.
  • java.xml — lavoro con XML.

Se non usi XML, il modulo java.xml non verrà nemmeno incluso nel tuo runtime.

Nelle grandi applicazioni e librerie

Indispensabile per i sistemi enterprise, dove decine di team sviluppano parti di un monorepo. La modularità riduce i conflitti e semplifica la manutenzione.

Nei progetti personali

Anche in un pet project i moduli aiutano ad allenare il pensiero architetturale e a tenere il codice in ordine, evitando “spaghetti”.

5. Panoramica rapida della sintassi: module-info.java

La cosa più importante — il file module-info.java

Il file module-info.java si trova alla radice dei sorgenti del modulo e ne dichiara confini e dipendenze.

module my.awesome.module {
    exports com.example.api;        // Pacchetto esportato
    requires java.sql;              // Dipendenza da un modulo standard
}

Parole chiave principali:

  • module <nome> — dichiara un modulo.
  • exports <pacchetto> — rende il pacchetto accessibile ad altri moduli.
  • requires <modulo> — dichiara una dipendenza da un altro modulo.

Esempio minimo:

module com.myproject.core {
    exports com.myproject.core.api;
}

Esempio con dipendenza:

module com.myproject.app {
    requires com.myproject.core;
    requires java.sql;
}

Funzionalità aggiuntive (in breve): per la riflessione c’è opens, per i servizi — uses e provides ... with ... (in dettaglio — nelle lezioni avanzate).

6. Sfumature utili

I moduli sono come “passaporti” per le classi. Prima qualsiasi classe con il visto public poteva “viaggiare” per l’applicazione. Ora serve anche il “passaporto” modulare — l’export del pacchetto (exports). Senza di esso la classe resta “a casa”.

Dependency hell è un termine reale. Se hai visto ClassNotFoundException: com.google.common.base.Strings, ci sei già passato. I moduli sono pensati per scacciare quei “diavoletti” con confini e dipendenze rigorose.

Tutta Java ora è modulare. Anche se non scrivi i tuoi moduli, la piattaforma è già suddivisa in decine di moduli. Prova il comando:

java --list-modules

Come influisce sullo sviluppo

Gestisci esplicitamente la visibilità del codice, e i pacchetti interni non diventano più accessibili a tutti “per caso”. IDE e compilatore intercettano gli errori prima: se si dimentica di dichiarare una dipendenza — il progetto non verrà compilato. La manutenzione dei sistemi grandi si semplifica: è più facile capire chi dipende da chi e cosa si romperà in caso di modifiche.

7. Errori tipici nel passaggio ai moduli

Errore n. 1: hai dimenticato di esportare il pacchetto, ma la classe è public. Hai dichiarato la classe come public, ma non hai esportato il suo pacchetto — gli altri moduli non potranno usarla. Il compilatore segnalerà: “Il pacchetto non è esportato dal modulo”.

Errore n. 2: non hai dichiarato la dipendenza tramite requires. Stai usando un tipo da un altro modulo, ma hai dimenticato di aggiungere requires in module-info.java. Risultato — errore di compilazione “il modulo non riesce a trovare la classe richiesta”.

Errore n. 3: nomi di moduli duplicati. In un progetto grande due moduli hanno ricevuto per errore lo stesso nome. La JVM questo non lo perdona — rinomina e mantieni un naming uniforme.

Errore n. 4: ti sei dimenticato dei moduli standard. Per esempio, usi JDBC, ma non hai aggiunto requires java.sql;. In Java 8 “si vedeva comunque”, dalla 9 in poi — no.

Errore n. 5: tentativo di usare una classe interna di un altro modulo. Se il pacchetto non è esportato, anche un public-classe resta invisibile dall’esterno. Esporta il pacchetto o sposta l’API in uno strato “pubblico”.

1
Compito
JAVA 25 SELF, livello 60, lezione 0
Bloccato
Cuore minimo del modulo 🫀
Cuore minimo del modulo 🫀
1
Compito
JAVA 25 SELF, livello 60, lezione 0
Bloccato
Apertura del gateway per la libreria 🔑
Apertura del gateway per la libreria 🔑
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION