CodeGym /Kursy /JAVA 25 SELF /module-info.java: składnia, tworzenie modułów

module-info.java: składnia, tworzenie modułów

JAVA 25 SELF
Poziom 60 , Lekcja 1
Dostępny

1. Czym jest module-info.java i gdzie się znajduje?

Moduł w Javie zawsze zaczyna się od pliku module-info.java. To swoisty „paszport” modułu, w którym deklarujesz jego nazwę, eksportowane pakiety oraz zależności od innych modułów.

Gdzie szukać?
Plik module-info.java powinien znajdować się w katalogu źródeł modułu, w jego katalogu głównym. Na przykład, jeśli struktura projektu jest taka:

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

Tutaj my.module.name — to nazwa modułu (o zasadach nazewnictwa za chwilę).

Bez tego pliku moduł po prostu nie jest uznawany za moduł!
Jeśli go nie ma — to zwykły „stary” projekt Java, nawet jeśli ma mnóstwo katalogów i klas.

2. Składnia module-info.java: elementy podstawowe

Spójrzmy od razu na przykład najprostszego pliku — to naprawdę nic strasznego!

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

Rozłóżmy to na części:

module my.module.name { ... }
To deklaracja modułu o nazwie my.module.name. Nazwa modułu — to unikalny identyfikator, który zazwyczaj pokrywa się z pakietem korzeniowym (na przykład com.example.app).
Ciekawostka: jeśli nazwiesz moduł java.base, kompilator nie będzie zachwycony. Nie próbuj podmieniać standardowych modułów.

exports com.example.api;
Ten wiersz mówi: „Ja, moduł, udostępniam wszystko, co znajduje się w pakiecie com.example.api”. Wszystko, co w tym pakiecie jest oznaczone jako public, będzie widoczne dla innych modułów. Cała reszta — tylko do użytku wewnętrznego.

requires java.sql;
A tutaj szczerze przyznajemy kompilatorowi: „Do działania potrzebuję standardowego modułu java.sql”. Bez tego kompilator nie pozwoli używać klas z tego modułu.

Dodatkowe słowa kluczowe (dla ogólnej orientacji)

  • opens <package>; — otwiera pakiet na refleksję (np. dla bibliotek serializacji takich jak Jackson).
  • uses <service-interface>; — informuje, że moduł używa danego serwisu (interfejsu).
  • provides <service-interface> with <implementation-class>; — oznacza, że moduł dostarcza implementację serwisu.

W tym wykładzie skupimy się na exports i requires — są potrzebne w zdecydowanej większości projektów zarówno edukacyjnych, jak i produkcyjnych.

3. Przykłady module-info.java

Przykład 1. Minimalny moduł

module com.example.hello {
    exports com.example.hello.api;
}
  • Ten moduł eksportuje wyłącznie pakiet com.example.hello.api.
  • Wszystko, co znajduje się np. w com.example.hello.internal, będzie ukryte przed innymi modułami, nawet jeśli znajdują się tam klasy public.

Przykład 2. Moduł z zależnością

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

Możemy używać JDBC, ale tylko dlatego, że uczciwie zadeklarowaliśmy zależność od java.sql.

Przykład 3. Wiele eksportowanych pakietów

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

Można eksportować dowolną liczbę pakietów (ale nie warto eksportować wszystkiego jak leci — na tym polega sens modułów!).

4. Ograniczenia i zasady

Nazwa modułu

  • Zazwyczaj pokrywa się z pakietem korzeniowym (na przykład com.example.app).
  • Nie może pokrywać się z nazwami standardowych modułów (java.base, java.sql itp.).
  • Nie może zawierać spacji, znaków specjalnych, zaczynać się cyfrą itd.
  • Rekomendacja: używaj odwróconej nazwy domeny swojej organizacji lub projektu, aby uniknąć konfliktów.

Jeden moduł — jeden module-info.java
W jednym module może być tylko jeden taki plik. Jeśli będą dwa — kompilator zrobi z tego „modułowy skandal”.

Eksport pakietu tylko z jednego modułu
Ten sam pakiet nie może być eksportowany przez dwa różne moduły. To jak dwa paszporty na jedno nazwisko — państwo tego nie zaakceptuje.

Pakiety wewnątrz modułu
Można eksportować tylko te pakiety, które faktycznie istnieją w strukturze źródeł tego modułu. Eksport nieistniejącego pakietu spowoduje błąd kompilacji.

5. Praktyka: tworzymy module-info.java w projekcie

Załóżmy, że mamy prosty projekt o następującej zawartości:

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

Krok 1. Tworzymy module-info.java

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

Krok 2. Próbujemy użyć klasy z pakietu internal w innym module

Załóżmy, że mamy drugi moduł com.example.app, który chce uzyskać dostęp do SecretSauce:

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

import com.example.greetings.internal.SecretSauce; // BŁĄD!

Wynik:
Kompilator powie: „Pakiet com.example.greetings.internal nie jest eksportowany przez moduł com.example.greetings”. Nawet jeśli klasa SecretSaucepublic, pozostaje niedostępna dla innych modułów.
To właśnie prawdziwa enkapsulacja na poziomie modułów!

Krok 3. Próbujemy nie zadeklarować requires

Jeśli w com.example.app nie napiszemy requires com.example.greetings;, a spróbujemy użyć klasy z com.example.greetings.api, kompilator zgłosi błąd:

package com.example.greetings.api is not visible

6. Typowe błędy podczas pracy z module-info.java

Błąd nr 1: Niezgodność nazwy modułu ze strukturą projektu.
Jeśli nazwiesz moduł com.example.app, a struktura katalogów będzie src/main/java/app, kompilator nie zrozumie, o co chodzi. Nazwa modułu zwykle pokrywa się z pakietem korzeniowym, a katalogi powinny to odzwierciedlać.

Błąd nr 2: Eksport wszystkiego jak leci.
Eksportować należy tylko to, co faktycznie powinno być widoczne dla innych modułów. Nie rób exports com.example; tylko dlatego, że „tak jest prościej”. To narusza enkapsulację.

Błąd nr 3: Zapomniano dodać requires.
Jeśli używasz klas z innego modułu lub standardowej biblioteki (na przykład java.sql), ale zapomniałeś zadeklarować zależność — pojawi się błąd kompilacji.

Błąd nr 4: Nieistniejący pakiet w exports.
Jeśli napisałeś exports com.example.foo;, a taki pakiet nie istnieje — kompilator zgłosi, że „eksportujesz powietrze”.

Błąd nr 5: Publiczne klasy w nieeksportowanym pakiecie.
Jeśli klasa jest oznaczona jako public, ale znajduje się w pakiecie, który nie jest eksportowany, będzie widoczna tylko wewnątrz modułu. To nie błąd, ale często zaskakuje początkujących.

Błąd nr 6: Wiele module-info.java w jednym module.
W jednym module powinien być tylko jeden plik module-info.java. Jeśli będą dwa — kompilator nie zdoła zbudować projektu.

1
Zadanie
JAVA 25 SELF, poziom 60, lekcja 1
Niedostępne
Podłączenie do cyfrowego oceanu danych 🌐
Podłączenie do cyfrowego oceanu danych 🌐
1
Zadanie
JAVA 25 SELF, poziom 60, lekcja 1
Niedostępne
Odblokowanie wieloaspektowej biblioteki 💎
Odblokowanie wieloaspektowej biblioteki 💎
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION