В чем разница между требованиями и транзитивными операторами в Java 9?

В чем разница между операторами requires и requires Transive в объявлении модуля?

Например, :

module foo {
    requires java.base;
    requires transitive java.compiler;
}

person Michał Szewczyk    schedule 30.09.2017    source источник


Ответы (6)


Обзор читабельности

Если модуль бар requires модуль напиток, то система модулей...

  • обеспечивает наличие напитка (так называемая надежная конфигурация)
  • позволяет bar читать напиток (называемый удобочитаемость)
  • позволяет коду в bar получать доступ к общедоступным классам в экспортированных пакетах в drink (называемых доступность)

Точно то же самое происходит, если 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 достаточно. Как это должно.

person Nicolai Parlog    schedule 30.09.2017
comment
Я нашел ваш пример с посетителем бара лучшим! Спасибо, что закрепили это знание в моем мозгу - person Jefferson Quesado; 18.11.2020

Основное различие между ними заключается в доступе зависимого модуля от одного к другому.

Если один модуль экспортирует пакет, содержащий тип, сигнатура которого относится к пакету во втором модуле, то объявление первого модуля должно включать 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
}
person Naman    schedule 30.09.2017

requires описывает процесс разрешения того, как модули зависят друг от друга.

Строка цитирования

Директива 'requires' (независимо от 'transitive') указывает, что один модуль зависит от другого модуля. Эффект модификатора 'transitive' заключается в том, чтобы дополнительные модули также зависели от другого модуля. Если модуль М «требует транзитивного N», то не только М зависит от N, но и любой модуль, который зависит от М, также зависит от N. Это позволяет рефакторингу М так, чтобы часть или все его содержимое можно было перенести в новый модуль N, не нарушая модули, которые имеют директиву «требует M».

Короче :

requires - Модуль M зависит от какого-то другого модуля N.

requires transitive - дополнительные модули неявно зависят от другого модуля. Например: если модуль M зависит от N, а другой модуль P зависит от M. Тогда он также неявно зависит от N.

person Ravi    schedule 30.09.2017

Николай подробно объяснил. Я просто привожу здесь конкретный пример из кода JDK. Рассмотрим модуль jdk.scripting.nashorn. Модуль-информация этого модуля выглядит следующим образом:

http://hg.openjdk.java.net/jdk9/dev/nashorn/file/17cc754c8936/src/jdk.scripting.nashorn/share/classes/module-info.java

У него есть эта строка:

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 является исключительно деталью реализации.

person A. Sundararajan    schedule 30.09.2017


Термин доступность неоднозначен: вы можете получить доступ к объектам без доступа к их типу. Если объект типа T лежит в пакете, который не экспортируется, и если в «экспортированном» коде есть метод, который возвращает T... Тогда при вызове этого метода вы получаете дескриптор этого объекта T (и вы можете вызывать на нем любые методы, относящиеся к любому типу, известному вашему коду).

читабельность также неоднозначна: это не означает, что ваш ClassLoader всегда не сможет загрузить (неэкспортированный) T-класс.

person bear    schedule 14.02.2018