CodeGym /Corsi /JAVA 25 SELF /module-info.java: sintassi, creazione dei moduli

module-info.java: sintassi, creazione dei moduli

JAVA 25 SELF
Livello 60 , Lezione 1
Disponibile

1. Che cos’è module-info.java e dove si trova?

Un modulo in Java inizia sempre dal file module-info.java. È una sorta di «passaporto» del modulo, dove dichiari il suo nome, i pacchetti esportati e le dipendenze da altri moduli.

Dove cercarlo?
Il file module-info.java deve trovarsi alla radice della cartella sorgenti del tuo modulo. Ad esempio, se la struttura del progetto è la seguente:

project-root/
└── src/
    └── my.module.name/
        ├── module-info.java
        └── com/
            └── example/
                └── api/
                    └── MyClass.java

Qui my.module.name è il nome del modulo (sulle regole di denominazione più avanti).

Senza questo file il modulo non è proprio un modulo!
Se manca — è un normale «vecchio» progetto Java, anche se pieno di cartelle e classi.

2. Sintassi di module-info.java: elementi fondamentali

Diamo subito un’occhiata a un esempio del file più semplice — non è nulla di spaventoso, davvero!

module my.module.name {
    exports com.example.api;
    requires java.sql;
}

Analizziamolo pezzo per pezzo:

module my.module.name { ... }
Questa è la dichiarazione di un modulo di nome my.module.name. Il nome del modulo è un identificatore univoco, di solito coincide con il package radice (per esempio, com.example.app).
Curiosità: se chiami il modulo java.base, il compilatore non sarà affatto contento. Meglio non provare a sostituire i moduli standard.

exports com.example.api;
Questa riga dice: «Io, modulo, sono pronto a condividere tutto ciò che si trova nel package com.example.api». Tutto ciò che si trova in questo package ed è marcato come public sarà visibile agli altri moduli. Tutto il resto — riservato all’uso interno.

requires java.sql;
Qui dichiariamo onestamente al compilatore: «Per funzionare ho bisogno del modulo standard java.sql». Senza di ciò il compilatore non ci permetterà di usare le classi di questo modulo.

Parole chiave aggiuntive (per completezza)

  • opens <package>; — apre il package alla reflection (per esempio, per librerie di serializzazione come Jackson).
  • uses <service-interface>; — indica che il modulo utilizza un certo servizio (interfaccia).
  • provides <service-interface> with <implementation-class>; — dichiara che il modulo fornisce un’implementazione del servizio.

In questa lezione ci concentreremo su exports e requires — servono nella stragrande maggioranza dei progetti didattici e di produzione.

3. Esempi di module-info.java

Esempio 1. Modulo minimale

module com.example.hello {
    exports com.example.hello.api;
}
  • Questo modulo esporta solo il package com.example.hello.api.
  • Tutto ciò che si trova, ad esempio, in com.example.hello.internal sarà nascosto agli altri moduli, anche se ci sono classi public.

Esempio 2. Modulo con dipendenza

module com.example.dbclient {
    exports com.example.db.api;
    requires java.sql;
}

Possiamo usare JDBC, ma solo perché abbiamo dichiarato esplicitamente la dipendenza da java.sql.

Esempio 3. Più package esportati

module com.example.library {
    exports com.example.library.api;
    exports com.example.library.utils;
}

Si possono esportare quanti package si vuole (ma non conviene esportare tutto indiscriminatamente — è proprio questo il senso dei moduli!).

4. Limitazioni e regole

Nome del modulo

  • Di solito coincide con il package radice (per esempio, com.example.app).
  • Non deve coincidere con i nomi dei moduli standard (java.base, java.sql, ecc.).
  • Non deve contenere spazi, caratteri speciali, iniziare con una cifra, ecc.
  • Raccomandazione: usa il nome di dominio rovesciato della tua organizzazione o del progetto per evitare conflitti.

Un modulo — un solo module-info.java
In un modulo può esserci un solo file di questo tipo. Se ce ne sono due — il compilatore ti farà una «scenata modulare».

Un package può essere esportato da un solo modulo
Lo stesso package non può essere esportato da due moduli diversi. È come avere due passaporti con lo stesso nome — lo Stato non lo approverebbe.

Package all’interno del modulo
Si possono esportare solo i package che esistono realmente nella struttura dei sorgenti di quel modulo. L’esportazione di un package inesistente comporta un errore di compilazione.

5. Pratica: creiamo module-info.java nel progetto

Supponiamo di avere un progetto semplice con il seguente contenuto:

project-root/
└── src/
    └── com.example.greetings/
        ├── module-info.java
        └── com/
            └── example/
                └── greetings/
                    ├── api/
                    │   └── Greeter.java
                    └── internal/
                        └── SecretSauce.java

Passo 1. Creiamo module-info.java

module com.example.greetings {
    exports com.example.greetings.api;
}

Passo 2. Proviamo a usare una classe dal package internal in un altro modulo

Supponiamo di avere un secondo modulo com.example.app che vuole accedere a SecretSauce:

module com.example.app {
    requires com.example.greetings;
}

import com.example.greetings.internal.SecretSauce; // ERRORE!

Risultato:
Il compilatore dirà: «Il package com.example.greetings.internal non è esportato dal modulo com.example.greetings». Anche se la classe SecretSauce è public, non è accessibile agli altri moduli.
Questa è la vera incapsulazione a livello di moduli!

Passo 3. Proviamo a non dichiarare requires

Se in com.example.app non scriviamo requires com.example.greetings; e proviamo a usare una classe da com.example.greetings.api, il compilatore genererà un errore:

package com.example.greetings.api is not visible

6. Errori tipici lavorando con module-info.java

Errore n. 1: il nome del modulo non corrisponde alla struttura del progetto.
Se chiami il modulo com.example.app ma la struttura delle cartelle è src/main/java/app, il compilatore non capirà cosa vuoi. Il nome del modulo di solito coincide con il package radice e le cartelle dovrebbero rifletterlo.

Errore n. 2: esportare tutto in blocco.
Bisogna esportare solo ciò che deve davvero essere visibile agli altri moduli. Non fare exports com.example; solo perché «è più semplice». Questo viola l’incapsulazione.

Errore n. 3: dimenticare di aggiungere requires.
Se usi classi da un altro modulo o dalla libreria standard (ad esempio, java.sql) ma ti dimentichi di dichiarare la dipendenza — otterrai un errore di compilazione.

Errore n. 4: package inesistente in exports.
Se scrivi exports com.example.foo; ma quel package non esiste — il compilatore dirà che stai «esportando aria».

Errore n. 5: classi public in un package non esportato.
Se una classe è dichiarata public ma si trova in un package che non è esportato, tale classe sarà visibile solo all’interno del modulo. Non è un errore, ma spesso sorprende i principianti.

Errore n. 6: più module-info.java in un solo modulo.
In un modulo deve esserci un solo file module-info.java. Se ce ne sono due — il compilatore non riuscirà a costruire il progetto.

Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION