CodeGym /Corsi /JAVA 25 SELF /Incapsulazione tramite moduli: esportazione e importazion...

Incapsulazione tramite moduli: esportazione e importazione

JAVA 25 SELF
Livello 60, Lezione 2
Disponibile

1. Esportazione dei pacchetti: chi vede il tuo codice?

Nel mondo prima di Java 9 era tutto semplice, ma non molto sicuro. Se una classe era public, chiunque poteva vederla avendo accesso al JAR o al classpath. Questo portava al fatto che classi interne e persino interi pacchetti potessero essere usati accidentalmente o intenzionalmente al di fuori del tuo progetto. Sì, si poteva nascondere qualcosa con private o package-private, ma se una classe è public, è accessibile a tutti.

Con i moduli, questo è cambiato. Ora, anche se una classe è dichiarata public, non la si può usare da un altro modulo se il pacchetto in cui si trova non è esportato esplicitamente tramite module-info.java.

Come appare nella pratica

Supponiamo che tu abbia la seguente struttura del progetto:

my-app/
  src/
    core/
      com/example/core/
        CoreService.java
        InternalHelper.java
      module-info.java
    app/
      com/example/app/
        Main.java
      module-info.java

Nel file core/module-info.java scrivi:

module core {
    exports com.example.core;
}

Allora solo le classi del pacchetto com.example.core saranno disponibili agli altri moduli. Tutto ciò che si trova in altri pacchetti (per esempio, com.example.core.internal) sarà invisibile — anche se lì ci sono classi public. Questo è l’incapsulamento modulare.

Mini demo: « visibile/invisibile »

All'interno di core:

// com/example/core/CoreService.java
package com.example.core;

public class CoreService {
    public void doWork() {
        System.out.println("Work done!");
    }
}

All'interno di app:

// com/example/app/Main.java
package com.example.app;

import com.example.core.CoreService;

public class Main {
    public static void main(String[] args) {
        CoreService service = new CoreService();
        service.doWork();
    }
}

Se rimuovi la riga exports com.example.core; da core/module-info.java, in compilazione otterrai l’errore:

error: package com.example.core is not visible

Anche se CoreService è public!

2. Import delle dipendenze: requires e isolamento rigoroso

In JPMS non puoi semplicemente usare una classe da un altro modulo. Devi dichiarare esplicitamente la dipendenza con requires.

Esempio

Nel file app/module-info.java:

module app {
    requires core;
}

Ora il modulo app può usare tutto ciò che il modulo core esporta.

Se dimentichi di scrivere requires core;, otterrai un errore di compilazione:

error: package com.example.core is not visible

oppure

error: cannot access CoreService

Punto importante

  • requires funziona a livello di moduli, non di pacchetti.
  • Non puoi «importare» un solo pacchetto — solo l'intero modulo esportato.

3. Confronto: moduli vs public/private

I moduli aggiungono un nuovo livello di incapsulamento che sta «sopra» classi e pacchetti.

Livello Cosa regola? Come funziona?
private Accesso all'interno della classe Solo all'interno di un file
package-private (predefinito) Accesso all'interno del pacchetto Tutte le classi nello stesso pacchetto
public Accesso per tutti Qualsiasi codice in qualsiasi punto
module Accesso tra moduli Solo i pacchetti esportati

Idea chiave:

  • Una classe può essere public, ma se il suo pacchetto non è esportato è accessibile solo all'interno del modulo.
  • Esportare un pacchetto con exports è come una «finestra» attraverso cui il tuo codice è visibile ad altri moduli.

Esempio: implementazione nascosta

// com/example/core/internal/SecretSauce.java
package com.example.core.internal;

public class SecretSauce {
    public void addMagic() {}
}

Se NON esporti il pacchetto com.example.core.internal in module-info.java, nessun modulo esterno potrà usare questa classe, anche se è public!

4. Esempio: core e app — API e implementazione

Consideriamo uno scenario tipico: il modulo core fornisce l’API, il modulo app la utilizza.

Struttura:

core/
  com/example/core/
    CoreAPI.java
  com/example/core/impl/
    CoreImpl.java
  module-info.java

app/
  com/example/app/
    Main.java
  module-info.java

core/module-info.java:

module core {
    exports com.example.core; // Solo API!
    // Non esportiamo com.example.core.impl
}

app/module-info.java:

module app {
    requires core;
}

com/example/core/CoreAPI.java:

package com.example.core;

public interface CoreAPI {
    void doSomething();
}

com/example/core/impl/CoreImpl.java:

package com.example.core.impl;

import com.example.core.CoreAPI;

public class CoreImpl implements CoreAPI {
    @Override
    public void doSomething() {
        System.out.println("Doing something!");
    }
}

com/example/app/Main.java:

package com.example.app;

import com.example.core.CoreAPI;
// import com.example.core.impl.CoreImpl; // Questo causerà un errore di compilazione!

public class Main {
    public static void main(String[] args) {
        // CoreImpl impl = new CoreImpl(); // Errore! Il pacchetto non è esportato.
        // Si può usare solo ciò che è visibile tramite l'API.
    }
}

Risultato:

  • Il modulo app vede solo ciò che core esporta.
  • L’implementazione (impl) è nascosta, anche se le classi lì sono public.

5. Pratica: giochiamo con export e visibilità

Passo 1: rimuovere l'export

In core/module-info.java commenta la riga:

// exports com.example.core;

Ora prova a compilare il progetto. Aspettati un errore di compilazione nel modulo app — non vedrà le classi da com.example.core.

Passo 2: esportare solo l'API

Ripristina la riga exports com.example.core;, ma non esportare com.example.core.impl. Prova nel modulo app a importare una classe da impl. Otterrai di nuovo un errore di compilazione — tutto regolare!

Passo 3: classe public in un pacchetto non esportato

Crea una classe public in un pacchetto non esportato. Prova a usarla da un altro modulo — non funzionerà. Questo è l’incapsulamento modulare in azione.

6. Come appare in un IDE e « in parole semplici »

  • Quando provi a importare una classe da un pacchetto non esportato, l’IDE evidenzierà un errore.
  • Il suggerimento spiegherà che il pacchetto non è esportato dal modulo.
  • Aggiungi exports per il pacchetto necessario — l’errore scomparirà.

Schema: livelli di accesso

+---------------------+
|      MODULO         |
|  (module-info.java) |
+---------------------+
       |
       v
+---------------------+
|     PACCHETTI       |
| (package, export)   |
+---------------------+
       |
       v
+---------------------+
|      CLASSI         |
| (public/private)    |
+---------------------+
       |
       v
+---------------------+
|   METODI/CAMPI      |
| (public/private)    |
+---------------------+

7. Dettagli importanti e particolarità

Export «solo per gli amici»: exports ... to

A volte è necessario esportare un pacchetto solo per moduli specifici (per esempio per test o estensioni speciali):

exports com.example.core.internal to my.special.module, my.test.module;

Ora solo i moduli indicati vedranno questo pacchetto.

Si possono esportare più pacchetti?

Sì! Basta aggiungere nuove righe exports:

exports com.example.core;
exports com.example.core.api;

Si può esportare «tutto»?

No. Nel sistema di moduli di Java specifichi sempre esplicitamente cosa esportare. È questa la sua forza.

8. Errori tipici quando si lavora con l'incapsulamento modulare

Errore n. 1: aspettarsi che una classe public sia sempre visibile.
Se il pacchetto non è esportato tramite module-info.java, la classe sarà invisibile per gli altri moduli, anche se è public. È la «trappola» più comune per i principianti.

Errore n. 2: hai dimenticato di dichiarare requires.
Se il tuo modulo usa classi da un altro modulo ma non ha scritto requires, otterrai un errore di compilazione. Ricordati di descrivere esplicitamente le dipendenze.

Errore n. 3: tentare di esportare lo stesso pacchetto da due moduli.
In JPMS ogni pacchetto può essere esportato da un solo modulo. Se violi questa regola, il compilatore ti fermerà.

Errore n. 4: struttura errata — module-info.java nel posto sbagliato.
Il file module-info.java deve trovarsi nella radice dei sorgenti del modulo, altrimenti il modulo non verrà riconosciuto.

Errore n. 5: dipendenze cicliche tra moduli.
Se il modulo A richiede B e B richiede A — otterrai un errore. Evita i cicli nel grafo delle dipendenze.

1
Compito
JAVA 25 SELF, livello 60, lezione 2
Bloccato
Officina di strumenti digitali e dimostrazione della loro potenza 🛠️
Officina di strumenti digitali e dimostrazione della loro potenza 🛠️
1
Compito
JAVA 25 SELF, livello 60, lezione 2
Bloccato
Custode dei segreti del servizio 🤫
Custode dei segreti del servizio 🤫
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION