1. Що таке module-info.java й де він розташований?
Модуль у Java завжди починається з файла module-info.java. Це своєрідний «паспорт» модуля, де ви оголошуєте його ім’я, експортовані пакети та залежності від інших модулів.
Де шукати?
Файл module-info.java має міститися в корені теки вихідних файлів вашого модуля. Наприклад, якщо у вас така структура проєкту:
project-root/
└── src/
└── my.module.name/
├── module-info.java
└── com/
└── example/
└── api/
└── MyClass.java
Тут my.module.name — це ім’я модуля (про правила найменування трохи згодом).
Без цього файла модуль просто не вважається модулем!
Якщо його немає — це звичайний «старий» Java‑проєкт, хай навіть із купою тек і класів.
2. Синтаксис module-info.java: основні елементи
Давайте одразу поглянемо на приклад найпростішого файла — це не страшно, чесно!
module my.module.name {
exports com.example.api;
requires java.sql;
}
Розберімо по частинах:
module my.module.name { ... }
Це оголошення модуля з ім’ям my.module.name. Ім’я модуля — це унікальний ідентифікатор, зазвичай збігається з кореневим пакетом (наприклад, com.example.app).
Цікавий факт: якщо ви назвете модуль java.base, компілятор не зрадіє. Не варто намагатися підмінювати стандартні модулі.
exports com.example.api;
Цей рядок каже: «Я, модуль, готовий поділитися всім, що міститься в пакеті com.example.api». Усе, що всередині цього пакета й позначене як public, буде видиме іншим модулям. Усе інше — суто для своїх.
requires java.sql;
А тут ми чесно зізнаємося компілятору: «Мені для роботи потрібен стандартний модуль java.sql». Без цього компілятор не дозволить використовувати класи з цього модуля.
Додаткові ключові слова (для загального розвитку)
- opens <package>; — відкриває пакет для рефлексії (наприклад, для бібліотек серіалізації на кшталт Jackson).
- uses <service-interface>; — повідомляє, що модуль використовує певний сервіс (інтерфейс).
- provides <service-interface> with <implementation-class>; — повідомляє, що модуль надає реалізацію сервісу.
У цій лекції ми зосередимося на exports і requires — вони потрібні у переважній більшості навчальних і бойових проєктів.
3. Приклади module-info.java
Приклад 1. Мінімальний модуль
module com.example.hello {
exports com.example.hello.api;
}
- Цей модуль експортує лише пакет com.example.hello.api.
- Усе, що лежить, наприклад, у com.example.hello.internal, буде приховане від інших модулів, навіть якщо там є public-класи.
Приклад 2. Модуль із залежністю
module com.example.dbclient {
exports com.example.db.api;
requires java.sql;
}
Ми можемо використовувати JDBC, але лише тому, що чесно оголосили залежність від java.sql.
Приклад 3. Кілька експортованих пакетів
module com.example.library {
exports com.example.library.api;
exports com.example.library.utils;
}
Можна експортувати скільки завгодно пакетів (але не варто експортувати все підряд — у цьому й сенс модулів!).
4. Обмеження та правила
Ім’я модуля
- Зазвичай збігається з кореневим пакетом (наприклад, com.example.app).
- Не повинно збігатися з іменами стандартних модулів (java.base, java.sql тощо).
- Не повинно містити пробілів, спеціальних символів, починатися з цифри тощо.
- Рекомендація: використовуйте зворотне доменне ім’я вашої організації або проєкту, щоб уникнути конфліктів.
Один модуль — один module-info.java
В одному модулі може бути лише один такий файл. Якщо їх два — компілятор влаштує вам «модульний скандал».
Експорт пакета лише з одного модуля
Один і той самий пакет не може експортуватися з двох різних модулів. Це якби у вас було два паспорти на одне ім’я — держава не схвалить.
Пакети всередині модуля
Можна експортувати лише ті пакети, які реально існують у структурі вихідних файлів цього модуля. Експорт неіснуючого пакета призведе до помилки компіляції.
5. Практика: створюємо module-info.java у проєкті
Припустімо, у нас є простий проєкт із таким вмістом:
project-root/
└── src/
└── com.example.greetings/
├── module-info.java
└── com/
└── example/
└── greetings/
├── api/
│ └── Greeter.java
└── internal/
└── SecretSauce.java
Крок 1. Створюємо module-info.java
module com.example.greetings {
exports com.example.greetings.api;
}
Крок 2. Пробуємо використати клас із internal‑пакета в іншому модулі
Припустімо, у нас є другий модуль com.example.app, який хоче отримати доступ до SecretSauce:
module com.example.app {
requires com.example.greetings;
}
import com.example.greetings.internal.SecretSauce; // ПОМИЛКА!
Результат:
Компілятор скаже: «Пакет com.example.greetings.internal не експортується модулем com.example.greetings». Навіть якщо клас SecretSauce — public, він недоступний іншим модулям.
Ось це й є справжня інкапсуляція на рівні модулів!
Крок 3. Пробуємо не оголосити requires
Якщо в com.example.app ми не напишемо requires com.example.greetings;, а спробуємо використати клас із com.example.greetings.api, компілятор видасть помилку:
package com.example.greetings.api is not visible
6. Типові помилки під час роботи з module-info.java
Помилка № 1: Невідповідність імені модуля та структури проєкту.
Якщо ви назвете модуль com.example.app, а структура тек буде src/main/java/app, компілятор не зрозуміє, чого ви від нього хочете. Ім’я модуля зазвичай збігається з кореневим пакетом, а теки мають це відображати.
Помилка № 2: Експорт усього підряд.
Експортувати потрібно лише те, що справді має бути видимим іншим модулям. Не слід робити exports com.example; тільки тому, що «так простіше». Це порушує інкапсуляцію.
Помилка № 3: Забули додати requires.
Якщо ви використовуєте класи з іншого модуля або стандартної бібліотеки (наприклад, java.sql), але забули оголосити залежність — буде помилка компіляції.
Помилка № 4: Неіснуючий пакет у exports.
Якщо ви написали exports com.example.foo;, а такого пакета немає — компілятор скаже, що ви «експортуєте повітря».
Помилка № 5: Публічні класи в неекспортованому пакеті.
Якщо клас оголошено public, але він лежить у пакеті, який не експортується, цей клас буде видимий лише всередині модуля. Це не помилка, але часто стає несподіванкою для новачків.
Помилка № 6: Кілька module-info.java в одному модулі.
В одному модулі має бути лише один файл module-info.java. Якщо їх два — компілятор не зможе зібрати проєкт.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ