В чем разница между операторами requires и requires Transive в объявлении модуля?
Например, :
module foo {
requires java.base;
requires transitive java.compiler;
}
В чем разница между операторами requires и requires Transive в объявлении модуля?
Например, :
module foo {
requires java.base;
requires transitive java.compiler;
}
Если модуль бар requires
модуль напиток, то система модулей...
Точно то же самое происходит, если bar requires transitive drink
- drink должен присутствовать, может быть прочитан и доступен. Фактически, для бара и напитка ключевое слово transitive
ничего не меняет.
На модули, зависящие от bar, влияет transitive
: любой модуль, который читает bar, также может читать drink. Другими словами, читабельность напитка подразумевается (именно поэтому это называется подразумеваемая удобочитаемость). Следствием этого является то, что customer может получить доступ к типам drink.
Таким образом, если bar requires transitive drink
и customer requires bar
, то клиент может читать напиток, даже если это явно не зависит от него.
Но почему? Представьте, что у вас есть модуль, общедоступный API которого принимает или возвращает тип другого модуля. Допустим, модуль bar публично возвращает экземпляры Drink
, интерфейса из модуля drink:
// in module _bar_
public class Bar {
// `Drink` comes from the module _drink_,
// which _bar_ requires
public Drink buyDrink() { /* ... */ }
}
В этом примере bar использует обычный requires
для напитка. Теперь предположим, что customer зависит от bar, поэтому весь его код может вызывать Bar::buyDrink
. Но что происходит, когда это происходит?
Модульная система жалуется, что клиент не читает напиток и, следовательно, не может получить доступ к Drink
. Чтобы это исправить, клиент также должен зависеть от напитка. Какая работа! Насколько бесполезен бар, который вы не можете использовать сразу?
По этой причине была введена подразумеваемая удобочитаемость: сделать модуль, который использует типы другого модуля в своем собственном общедоступном API, мгновенно используемым, без вызывающего объекта, который должен искать и запрашивать все задействованные модули.
Таким образом, если bar requires transitive drink
, покупатель может начать покупать напитки без необходимости require drink
- require bar
достаточно. Как это должно.
Основное различие между ними заключается в доступе зависимого модуля от одного к другому.
Если один модуль экспортирует пакет, содержащий тип, сигнатура которого относится к пакету во втором модуле, то объявление первого модуля должно включать
requires transitive
зависимость от второго модуля. Это гарантирует, что другие модули, зависящие от первого модуля, смогут автоматически читать второй модуль и, следовательно, получать доступ ко всем типам в экспортированных пакетах этого модуля.
Итак, скажем, для вашего варианта использования: -
module foo {
requires java.base;
requires transitive java.compiler;
}
~> Любой модуль, который зависит от модуля foo
, автоматически прочитает модуль java.compiler
~> С другой стороны, чтобы получить доступ к модулю java.base
, они должны снова указать предложение requires
.
module bar {
requires foo; // java.compiler is available to read
requires java.base; // still required
}
requires
описывает процесс разрешения того, как модули зависят друг от друга.
Директива 'requires' (независимо от 'transitive') указывает, что один модуль зависит от другого модуля. Эффект модификатора 'transitive' заключается в том, чтобы дополнительные модули также зависели от другого модуля. Если модуль М «требует транзитивного N», то не только М зависит от N, но и любой модуль, который зависит от М, также зависит от N. Это позволяет рефакторингу М так, чтобы часть или все его содержимое можно было перенести в новый модуль N, не нарушая модули, которые имеют директиву «требует M».
Короче :
requires
- Модуль M зависит от какого-то другого модуля N.
requires transitive
- дополнительные модули неявно зависят от другого модуля. Например: если модуль M зависит от N, а другой модуль P зависит от M. Тогда он также неявно зависит от N.
Николай подробно объяснил. Я просто привожу здесь конкретный пример из кода JDK. Рассмотрим модуль jdk.scripting.nashorn. Модуль-информация этого модуля выглядит следующим образом:
У него есть эта строка:
requires transitive java.scripting;
Это связано с тем, что jdk.scripting.nashorn собственный API модуля в Пакет jdk.scripting.api.scripting принимает/возвращает типы из javax.script пакета java.scripting. Таким образом, jdk.scripting.nashorn сообщает JMPS, что любой модуль, зависящий от jdk.scripting.nashorn, автоматически зависит и от модуля java.scripting!
Теперь тот же модуль jdk.scripting.nashorn использует эту строку:
requires jdk.dynalink;
для другого модуля jdk.dynalink. Это связано с тем, что ни один из экспортированных пакетов ("API") из модуля jdk.scripting.nashorn не использует типы из модуля jdk.dynalink. Использование jdk.dynalink в jdk.scripting.nashorn является исключительно деталью реализации.
Спецификация языка Java для Java 9 объясняет это очень просто. Из раздела модульные зависимости а>:
Директива
requires
указывает имя модуля, от которого зависит текущий модуль....
За ключевым словом
requires
может следовать модификаторtransitive
. Это приводит к тому, что любой модуль, которыйrequires
является текущим модулем, имеет неявно объявленную зависимость от модуля, указанного в директивеrequires transitive
.
Другими словами:
requires
модуль Y,requires transitive
модуль Z,requires
модуль Z.Термин доступность неоднозначен: вы можете получить доступ к объектам без доступа к их типу. Если объект типа T лежит в пакете, который не экспортируется, и если в «экспортированном» коде есть метод, который возвращает T... Тогда при вызове этого метода вы получаете дескриптор этого объекта T (и вы можете вызывать на нем любые методы, относящиеся к любому типу, известному вашему коду).
читабельность также неоднозначна: это не означает, что ваш ClassLoader всегда не сможет загрузить (неэкспортированный) T-класс.