JavaRush /Курси /JAVA 25 SELF /module-info.java: синтаксис, створення модулів

module-info.java: синтаксис, створення модулів

JAVA 25 SELF
Рівень 60 , Лекція 1
Відкрита

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». Навіть якщо клас SecretSaucepublic, він недоступний іншим модулям.
Ось це й є справжня інкапсуляція на рівні модулів!

Крок 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. Якщо їх два — компілятор не зможе зібрати проєкт.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ