CodeGym /Kursy /JAVA 25 SELF /Enkapsulacja przez moduły: eksport i import

Enkapsulacja przez moduły: eksport i import

JAVA 25 SELF
Poziom 60, Lekcja 2
Dostępny

1. Eksport pakietów: kto widzi twój kod?

W świecie przed Java 9 wszystko było proste. Tylko niezbyt bezpieczne. Jeśli klasa była public, każdy mógł ją zobaczyć, mając dostęp do JAR lub classpath. Prowadziło to do tego, że wewnętrzne klasy, a nawet całe pakiety mogły być przypadkowo lub celowo używane poza twoim projektem. Tak, można było coś ukryć za pomocą private lub package-private, ale jeśli klasa jest public — jest dostępna dla wszystkich.

Pojawienie się modułów to zmieniło. Teraz nawet jeśli klasa jest zadeklarowana jako public, nie można jej użyć z innego modułu, jeśli pakiet, w którym się znajduje, nie jest jawnie eksportowany przez module-info.java.

Jak to wygląda w praktyce

Załóżmy, że masz taką strukturę projektu:

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

W pliku core/module-info.java piszesz:

module core {
    exports com.example.core;
}

Wtedy tylko klasy z pakietu com.example.core będą dostępne dla innych modułów. Wszystko, co znajduje się w innych pakietach (na przykład com.example.core.internal), będzie niewidoczne — nawet jeśli są tam klasy public. To właśnie enkapsulacja modułowa.

Mini-demo: „widoczne/niewidoczne”

Wewnątrz core:

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

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

Wewnątrz 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();
    }
}

Jeśli usuniesz linię exports com.example.core; z core/module-info.java, to przy kompilacji dostaniesz błąd:

error: package com.example.core is not visible

Nawet jeśli CoreService jest public!

2. Import zależności: requires i ścisła izolacja

W JPMS nie można ot tak użyć klasy z innego modułu. Trzeba jawnie zadeklarować zależność przez requires.

Przykład

W pliku app/module-info.java:

module app {
    requires core;
}

Teraz moduł app może używać wszystkiego, co eksportuje moduł core.

Jeśli zapomnisz dodać requires core;, dostaniesz błąd kompilacji:

error: package com.example.core is not visible

albo

error: cannot access CoreService

Ważna uwaga

  • requires działa na poziomie modułów, a nie pakietów.
  • Nie możesz „zaimportować” tylko jednego pakietu — jedynie cały eksportowany moduł.

3. Porównanie: moduły kontra public/private

Moduły dodają nowy poziom enkapsulacji, który znajduje się „nad” klasami i pakietami.

Poziom Co kontroluje? Jak działa?
private Dostęp wewnątrz klasy Tylko w obrębie jednego pliku
package-private (domyślnie) Dostęp w obrębie pakietu Wszystkie klasy w jednym pakiecie
public Dostęp dla wszystkich Dowolny kod w dowolnym miejscu
module Dostęp między modułami Tylko eksportowane pakiety

Kluczowa idea:

  • Klasa może być public, ale jeśli jej pakiet nie jest eksportowany, jest dostępna tylko wewnątrz modułu.
  • Eksport pakietu przez exports to jak „okno”, przez które twój kod jest widoczny dla innych modułów.

Przykład: ukryta implementacja

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

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

Jeśli NIE eksportujesz pakietu com.example.core.internal w module-info.java, żaden zewnętrzny moduł nie będzie mógł użyć tej klasy, nawet jeśli jest public!

4. Przykład: core i app — API i implementacja

Rozważmy typowy scenariusz: moduł core dostarcza API, moduł app — używa go.

Struktura:

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; // Tylko API!
    // Nie eksportujemy 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; // To wywoła błąd kompilacji!

public class Main {
    public static void main(String[] args) {
        // CoreImpl impl = new CoreImpl(); // Błąd! Pakiet nie jest eksportowany.
        // Można używać tylko tego, co jest widoczne przez API.
    }
}

Rezultat:

  • Moduł app widzi tylko to, co eksportuje core.
  • Implementacja (impl) jest ukryta, nawet jeśli znajdują się tam klasy public.

5. Praktyka: ćwiczymy exports i widoczność

Krok 1: usuwamy exports

W core/module-info.java zakomentuj linię:

// exports com.example.core;

Teraz spróbuj skompilować projekt. Oczekuj błędu kompilacji w module app — nie zobaczy on klas z com.example.core.

Krok 2: eksportujemy tylko API

Przywróć linię exports com.example.core;, ale nie eksportuj com.example.core.impl. Spróbuj w module app zaimportować klasę z impl. Znów dostaniesz błąd kompilacji — wszystko uczciwie!

Krok 3: klasa publiczna w nieeksportowanym pakiecie

Utwórz klasę public w nieeksportowanym pakiecie. Spróbuj użyć jej z innego modułu — nie uda się. To właśnie enkapsulacja modułowa w działaniu.

6. Jak to wygląda w IDE i „na palcach”

  • Przy próbie zaimportowania klasy z nieeksportowanego pakietu IDE podkreśli błąd.
  • Podpowiedź wyjaśni, że pakiet nie jest eksportowany przez moduł.
  • Dodaj exports dla potrzebnego pakietu — błąd zniknie.

Schemat: poziomy dostępu

+---------------------+
|        MODUŁ        |
|  (module-info.java) |
+---------------------+
       |
       v
+---------------------+
|       PAKIETY       |
| (package, exports)  |
+---------------------+
       |
       v
+---------------------+
|        KLASY        |
|  (public/private)   |
+---------------------+
       |
       v
+---------------------+
|     METODY/POLA     |
|  (public/private)   |
+---------------------+

7. Ważne niuanse i szczegóły

Eksport „tylko dla przyjaciół”: exports ... to

Czasem trzeba eksportować pakiet tylko dla określonych modułów (np. dla testów lub specjalnych rozszerzeń):

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

Teraz tylko wskazane moduły zobaczą ten pakiet.

Czy można eksportować kilka pakietów?

Tak! Po prostu dodawaj kolejne linie exports:

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

Czy można eksportować „wszystko”?

Nie. W modularnym systemie Javy zawsze jawnie wskazujesz, co eksportować. Na tym polega jego siła.

8. Typowe błędy przy pracy z enkapsulacją modułową

Błąd nr 1: Oczekiwanie, że klasa public zawsze jest widoczna.
Jeśli pakiet nie jest eksportowany przez module-info.java, klasa będzie niewidoczna dla innych modułów, nawet jeśli jest public. To najczęstsza „pułapka” dla początkujących.

Błąd nr 2: Zapomniano zadeklarować requires.
Jeśli twój moduł używa klas z innego modułu, ale nie dodał requires, dostaniesz błąd kompilacji. Pamiętaj, aby jawnie opisywać zależności.

Błąd nr 3: Próba eksportu tego samego pakietu z dwóch modułów.
W JPMS każdy pakiet może być eksportowany tylko przez jeden moduł. Jeśli złamiesz tę zasadę, kompilator cię zatrzyma.

Błąd nr 4: Naruszenie struktury — module-info.java w złym miejscu.
Plik module-info.java musi leżeć w katalogu źródeł modułu (korzeniu jego źródeł), w przeciwnym razie moduł nie zostanie rozpoznany.

Błąd nr 5: Cykliczne zależności między modułami.
Jeśli moduł A wymaga B, a B wymaga A — dostaniesz błąd. Unikaj cykli w grafie zależności.

1
Zadanie
JAVA 25 SELF, poziom 60, lekcja 2
Niedostępne
Warsztat cyfrowych narzędzi i pokaz ich mocy 🛠️
Warsztat cyfrowych narzędzi i pokaz ich mocy 🛠️
1
Zadanie
JAVA 25 SELF, poziom 60, lekcja 2
Niedostępne
Strażnik sekretów serwisu 🤫
Strażnik sekretów serwisu 🤫
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION