Глава 7. Пакеты и модули
Оглавление
Программы организованы как наборы пакетов. Членами пакета (§7.1) являются классы и интерфейсы, которые объявлены в единицах компиляции пакета, и подпакеты, которые могут содержать единицы компиляции и подпакеты.
Каждый пакет имеет свой собственный набор имён для классов и интерфейсов, что помогает предотвратить конфликты имён. Структура имён для пакетов иерархическая.
Если набор пакетов достаточно сплочён, то пакеты могут быть сгруппированы в модуль. Модуль классифицирует некоторые или все свои пакеты как экспортированные, что означает, что к их классам и интерфейсам можно получить доступ из кода вне модуля. Если пакет не экспортирован модулем, то только код внутри модуля может получить доступ к его классам и интерфейсам. Кроме того, если код в модуле хочет получить доступ к пакетам, экспортированным другим модулем, то первый модуль должен явно зависеть от второго модуля. Таким образом, модуль управляет тем, как его пакеты используют другие модули (указанием зависимостей) и управляет тем, как другие модули используют его пакеты (указанием, какие пакеты экспортированы).
Модули и пакеты могут храниться в файловой системе или в базе данных (§7.2). Модули и пакеты, хранящиеся в файловой системе, могут иметь определённые ограничения на организацию своих единиц компиляции, чтобы позволить простой реализации легко находить объявления модулей, классов и интерфейсов.
Код в единице компиляции автоматически имеет доступ ко всем классам и интерфейсам, объявленным в его пакете, а также автоматически импортирует все public классы и интерфейсы, объявленные в предопределённом пакете java.lang.
Класс или интерфейс верхнего уровня доступен (§6.6) вне пакета, который его объявляет, только если класс или интерфейс объявлен public. Класс или интерфейс верхнего уровня доступен вне модуля, который его объявляет, только если класс или интерфейс объявлен public и является членом экспортированного пакета. Класс или интерфейс, который объявлен public, но не является членом экспортированного пакета, доступен только коду внутри модуля.
Для небольших программ и обычной разработки пакет может быть безымянным (§7.4.2) или иметь простое имя, но если код должен быть широко распространён, следует выбирать уникальные имена пакетов, используя полные имена. Это может предотвратить конфликты, которые могли бы возникнуть, если бы две группы разработчиков выбрали одно и то же имя пакета, и эти пакеты впоследствии были бы использованы в одной программе.
Членами пакета являются его подпакеты и все классы верхнего уровня (§8 (Классы)) и интерфейсы верхнего уровня (§9 (Интерфейсы)), объявленные во всех единицах компиляции (§7.3) пакета.
Например, в API платформы Java SE:
-
Пакет
javaимеет подпакетыawt,applet,io,lang,netиutil, но без единиц компиляции. -
Пакет
java.awtимеет подпакет с именемimage, а также ряд единиц компиляции, содержащих объявления классов и интерфейсов.
Если полное имя (§6.7) пакета – P, и Q является подпакетом P, то P.Q – полное имя подпакета и, более того, обозначает пакет.
В пакете не может быть двух членов с одинаковым именем, в противном случае возникает ошибка времени компиляции.
Вот некоторые примеры:
-
Поскольку пакет
java.awtимеет подпакетimage, он не может (и не содержит) объявление класса или интерфейса с именемimage. -
Если есть пакет с именем
mouseи член-классButtonв этом пакете (который затем можно обозначить какmouse.Button), то не может быть пакета с полным именемmouse.Buttonилиmouse.Button.Click. -
Если
com.nighthacks.java.jag– полное имя класса, то не может быть пакета, чьё полное имя равно либоcom.nighthacks.java.jag, либоcom.nighthacks.java.jag.scrabble.
Однако члены разных пакетов могут иметь одинаковые простые имена. Например, можно объявить пакет:
package vector;
public class Vector { Object[] vec; }
который имеет как члена класс public с именем Vector, даже если пакет java.util также объявляет класс с именем Vector. Эти два класса различны, что отражается в том, что у них разные полные имена (§6.7). Полное имя этого примера Vector – vector.Vector, в то время как java.util.Vector – полное имя класса Vector, включенного в Java SE Platform. Поскольку пакет vector содержит класс с именем Vector, он не может также иметь подпакет с именем Vector.
Иерархическая структура имён для пакетов предназначена для удобной организации связанных пакетов в соответствии с традициями, но не имеет значения само по себе, кроме запрета наличия в пакете подпакета с тем же простым именем, что и класс или интерфейс верхнего уровня (§7.6), объявленный в этом пакете.
Например, нет специальных отношений доступа между пакетом с именем oliver и другим пакетом с именем oliver.twist, или между пакетами с именами evelyn.wood и evelyn.waugh. То есть, код в пакете с именем oliver.twist не имеет лучшего доступа к классам и интерфейсам, объявленным в пакете oliver, чем код в любом другом пакете.
Каждая хост-система определяет, как создаются и хранятся модули, пакеты и единицы компиляции.
Каждая хост-система определяет, какие единицы компиляции являются наблюдаемыми в конкретной компиляции (§7.3). Каждая хост-система также определяет, какие наблюдаемые единицы компиляции связаны с модулем. Наблюдаемость единиц компиляции, связанных с модулем, определяет, какие модули наблюдаемы (§7.7.3) и какие пакеты видимы внутри этих модулей (§7.4.3).
Хост-система свободна определить, что единица компиляции, содержащая объявление модуля, фактически не наблюдаема и, следовательно, не связана с объявленным в ней модулем. Это позволяет компилятору выбрать, какой каталог на modulesourcepath "на самом деле" является воплощением данного модуля. Однако, если хост-система определяет, что единица компиляции, содержащая объявление модуля, является наблюдаемой, то §7.4.3 предписывает, что единица компиляции должна быть связана с объявленным в ней модулем, а не с любым другим модулем.
Хост-система свободна определить, что единица компиляции, содержащая объявление класса или интерфейса, (во-первых) наблюдаема и (во-вторых) связана с безымянным модулем или автоматическим модулем — несмотря на то, что объявление безымянного или автоматического модуля отсутствует в какой-либо единице компиляции, наблюдаемой или нет.
В простых реализациях платформы Java SE пакеты и единицы компиляции могут храниться в локальной файловой системе. Другие реализации могут хранить их с помощью распределённой файловой системы или какой-либо базы данных.
Если хост-система хранит пакеты и единицы компиляции в базе данных, то база данных не должна накладывать необязательные ограничения (§7.6) на единицы компиляции, допустимые в файловых реализациях.
Например, система, использующая базу данных для хранения пакетов, может не применять ограничение на максимальное количество одного публичного класса или интерфейса на единицу компиляции.
Тем не менее, системы, использующие базу данных, должны предоставить возможность преобразования программы в форму, соответствующую ограничениям, для целей экспорта в файловые реализации.
В качестве крайне простого примера хранения пакетов в файловой системе все пакеты и исходный и двоичный код проекта могут храниться в одном каталоге и его подкаталогах. Каждый непосредственный подкаталог этого каталога будет представлять собой пакет верхнего уровня, то есть пакет, полное имя которого состоит из одного простого имени. Каждый последующий уровень подкаталогов будет представлять собой подпакет пакета, представленного содержащим каталогом, и так далее.
Каталог может содержать следующие непосредственные подкаталоги:
com gls jag java wnj
где каталог java будет содержать пакеты платформы Java SE; каталоги jag, gls и wnj могут содержать пакеты, которые создали трое авторов этого спецификации для личного использования и совместного использования в этой небольшой группе; а каталог com будет содержать пакеты, полученные от компаний, которые использовали соглашения, описанные в §6.1, для генерации уникальных имён пакетов.
Продолжая пример, каталог java будет содержать, среди прочего, следующие подкаталоги:
applet awt io lang net util
соответствующие пакетам java.applet, java.awt, java.io, java.lang, java.net и java.util, определённым как часть API платформы Java SE.
Продолжая пример, если мы заглянем внутрь каталога util, мы можем увидеть следующие файлы:
BitSet.java Observable.java BitSet.class Observable.class Date.java Observer.java Date.class Observer.class ...
где каждый из файлов .java содержит исходный код для единицы компиляции (§7.3), содержащей определение класса или интерфейса, двоичная скомпилированная форма которого содержится в соответствующем файле .class.
При такой простой организации пакетов реализация платформы Java SE преобразует имя пакета в имя пути путём конкатенации компонентов имени пакета, помещая разделитель имени файла (индикатор каталога) между смежными компонентами.
Например, если эта простая организация использовалась на операционной системе, где разделитель имени файла — /, имя пакета:
jag.scrabble.board
преобразуется в имя каталога:
jag/scrabble/board
Компонент имени пакета или имя класса может содержать символ, который не может корректно отображаться в обычном имени каталога хост-файловой системы, например, символ Юникода на системе, допускающей только символы ASCII в именах файлов. По соглашению, символ можно экранировать, используя, скажем, символ @, за которым следуют четыре шестнадцатеричных цифры, задающие числовое значение символа, как в экранировании \uxxxx (§3.3).
В соответствии с этим соглашением, имя пакета:
children.activities.crafts.papierM\u00e2ch\u00e9
которое также можно записать с помощью полного Юникода как:
children.activities.crafts.papierMâché
может быть сопоставлено с именем каталога:
children/activities/crafts/papierM@00e2ch@00e9
Если символ @ не является допустимым символом в имени файла для данной файловой системы хоста, то вместо него можно использовать какой-либо другой символ, который не является допустимым в идентификаторе.
CompilationUnit — это целевой символ (§2.1) для синтаксической грамматики (§2.3) программ Java. Он определяется следующим произведением:
Обычная единица компиляции состоит из трёх частей, каждая из которых необязательна:
-
Декларация
package(§7.4), предоставляющая полное квалифицированное имя (§6.7) пакета, к которому принадлежит единица компиляции.Единица компиляции без
packageобъявления принадлежит безымянному пакету (§7.4.2). -
Декларации
import(§7.5), которые позволяют ссылаться на классы и интерфейсы из других пакетов, а также наstaticчлены классов и интерфейсов, используя их простые имена. -
Декларации классов и интерфейсов высшего уровня (§7.6).
Модульная единица компиляции состоит из объявления module (§7.7), которое необязательно предваряется import объявлениями. Эти import объявления позволяют ссылаться на классы и интерфейсы из пакетов данного модуля и других модулей, а также static члены классов и интерфейсов, используя их простые имена внутри объявления module.
Каждая единица компиляции неявно импортирует все public классы или интерфейсы, объявленные в предопределённом пакете java.lang, как если бы объявление import java.lang.*; появилось в начале каждой единицы компиляции сразу после любого объявления package. В результате имена всех этих классов и интерфейсов доступны как простые имена в каждой единице компиляции.
Система-хост определяет, какие единицы компиляции являются наблюдаемыми, за исключением единиц компиляции в предопределённом пакете java и его подпакетах lang и io, которые всегда наблюдаемы.
Каждая наблюдаемая единица компиляции может быть связана с модулем следующим образом:
-
Система-хост может определить, что наблюдаемая обычная единица компиляции связана с модулем, выбранным системой-хостом, за исключением (i) обычных единиц компиляции в предопределённом пакете
javaи его подпакетахlangиio, которые все связаны с модулемjava.base, и (ii) любой обычной единицы компиляции в безымянном пакете, которая связана с модулем, как указано в §7.4.2. -
Система-хост должна определить, что наблюдаемая модульная единица компиляции связана с модулем, объявленным модульной единицей компиляции.
Наблюдаемость единицы компиляции влияет на наблюдаемость её пакета (§7.4.3), а связывание наблюдаемой единицы компиляции с модулем влияет на наблюдаемость этого модуля (§7.7.6).
При компиляции модульных и обычных единиц компиляции, связанных с модулем M, система-хост должна соблюдать зависимости, указанные в декларации M. В частности, система-хост должна ограничить наблюдаемые обычные единицы компиляции только теми, которые видимы для M. Обычные единицы компиляции, которые видны для M, являются наблюдаемыми обычными единицами компиляции, связанными с модулями, которые читаются M. Модули, читаемые M, задаются результатом разрешения, как описано в спецификации пакета java.lang.module, с M в качестве единственного корневого модуля. Система-хост должна выполнить разрешение для определения модулей, читаемых M; ошибка компиляции возникает, если разрешение терпит неудачу по любой причине, описанной в спецификации пакета java.lang.module.
Отношение читаемости является рефлексивным, поэтому M читает сам себя, и, следовательно, все модульные и обычные единицы компиляции, связанные с M, видны для M.
Модули, читаемые M, управляют пакетами, которые уникально видны для M (§7.4.3), что, в свою очередь, управляет как пакетами высшего уровня в области видимости, так и значением имён пакетов для кода в модульных и обычных единицах компиляции, связанных с M (§6.3, §6.5.3, §6.5.5).
Указанные выше правила гарантируют, что имена пакетов и типов, используемые в аннотациях в модульной единице компиляции (в частности, аннотации, применённые к декларации модуля), интерпретируются так, как если бы они появились в обычной единице компиляции, связанной с модулем.
Классы и интерфейсы, объявленные в разных обычных единицах компиляции, могут ссылаться друг на друга, включая циклические ссылки. Компилятор Java должен обеспечить компиляцию всех таких классов и интерфейсов одновременно.
Объявление пакета появляется внутри обычного модуля компиляции, чтобы указать пакет, к которому принадлежит модуль компиляции.
Объявление пакета в обычном модуле компиляции указывает имя (§6.2) пакета, к которому принадлежит модуль компиляции.
Имя пакета, упомянутое в объявлении пакета, должно быть полным квалифицированным именем пакета (§6.7).
Область действия и перекрытие объявления пакета указаны в §6.3 и §6.4.
Правила, касающиеся модификаторов аннотаций для объявления пакета, указаны в §9.7.4 и §9.7.5.
Для данного пакета разрешено не более одного объявления пакета с аннотациями.
Способ, которым это ограничение применяется, должен в необходимости отличаться от реализации к реализации. Следующая схема очень рекомендуется для реализаций, основанных на файловой системе: единственное объявление пакета с аннотациями, если оно существует, помещается в файл исходного кода, называемый package-info.java, в каталоге, содержащем исходные файлы для пакета. Этот файл не содержит исходный код класса, называемого package-info; на самом деле для него это было бы незаконно, поскольку package-info не является допустимым идентификатором. Обычно package-info.java содержит только объявление пакета, непосредственно перед которым расположены аннотации пакета. Хотя файл технически может содержать исходный код одного или нескольких классов с доступом к пакету, это было бы очень плохой формой.
Рекомендуется, чтобы package-info.java, если оно есть, заняло место package.html для javadoc и других аналогичных систем генерации документации. Если этот файл присутствует, инструмент генерации документации должен искать комментарий к документации пакета непосредственно перед объявлением (возможно, с аннотациями) package в package-info.java. Таким образом, package-info.java становится единственным хранилищем аннотаций и документации на уровне пакета. Если в будущем потребуется добавить любую другую информацию на уровне пакета, этот файл должен оказаться удобным местом для этой информации.
Обычный модуль компиляции, не имеющий объявления пакета, но имеющий по крайней мере один другой тип объявления, является частью пакета без имени.
Пакеты без имени предоставляются платформой Java SE в основном для удобства при разработке небольших или временных приложений или при только начальном этапе разработки.
Пакет без имени не может иметь подпакеты, так как синтаксис объявления пакета всегда включает ссылку на именованный пакет верхнего уровня.
Реализация платформы Java SE должна поддерживать по крайней мере один пакет без имени. Реализация может поддерживать более одного пакета без имени, но это не обязательно. Которые обычные модули компиляции находятся в каждом пакете без имени, определяется системой хоста.
Система хоста должна связывать обычные модули компиляции в пакете без имени с безымянным модулем (§7.7.5), а не именованным модулем.
Пример 7.4.2-1. Пакет без имени
Модуль компиляции:
class FirstCall {
public static void main(String[] args) {
System.out.println("Mr. Watson, come here. "
+ "I want you.");
}
}
определяет очень простой модуль компиляции как часть пакета без имени.
В реализациях платформы Java SE, которые используют иерархическую файловую систему для хранения пакетов, одной типичной стратегией является связывание пакета без имени с каждым каталогом; только один пакет без имени наблюдается в одно время, а именно тот, который связан с "текущей рабочей директорией". Точное значение "текущей рабочей директории" зависит от системы хоста.
Пакет наблюдаем тогда и только тогда, когда выполняется хотя бы одно из следующих условий:
-
Обычный модуль компиляции, содержащий объявление пакета, наблюдаем (§7.3).
-
Подпакет пакета наблюдаем.
Пакеты java, java.lang и java.io всегда наблюдаемы.
Это можно заключить из правила выше и из правил наблюдаемых модулей компиляции следующим образом. Предварительно определённый пакет java.lang объявляет класс Object, поэтому модуль компиляции для Object всегда наблюдаем (§7.3). Следовательно, пакет java.lang наблюдаем, а также пакет java. Кроме того, поскольку Object наблюдаем, тип массива Object[] неявно существует. Его суперинтерфейс java.io.Serializable (§10.1) также существует, следовательно, пакет java.io наблюдаем.
Пакет видим для модуля M тогда и только тогда, когда обычный модуль компиляции, содержащий объявление пакета, виден для M.
Видимость пакета подразумевает, что пакет наблюдаем полезным образом для данного модуля. Обычно не имеет смысла знать, что пакет P наблюдаем только потому, что подпакет P.Q наблюдаем. Например, предположим, что P.Q наблюдаем (в модуле M1), а P.R наблюдаем (в модуле M2); тогда P наблюдаем, но где? В M1, или M2, или в обоих? Вопрос избыточен; во время компиляции модуля N, который требует только M1, важно, что P.Q наблюдаем, но неважно, что P наблюдаем.
Пакет уникально виден модулю M тогда и только тогда, когда выполняется одно из следующих условий:
-
Обычный модуль компиляции, связанный с
M, содержит объявление пакета, иMне считывает ни один другой модуль, который экспортирует пакет вM. -
Ни один обычный модуль компиляции, связанный с
M, не содержит объявления пакета, иMсчитывает ровно один другой модуль, который экспортирует пакет вM.
Объявление импорта позволяет ссылаться на именованный класс, интерфейс или static член с помощью простого имени (§6.2), состоящего из одного идентификатора.
Без соответствующего объявления импорта ссылка на класс или интерфейс, объявленный в другом пакете, или на static член другого класса или интерфейса, обычно должна использовать полное имя (§6.7).
-
Объявление импорта одного типа (§7.5.1) импортирует один именованный класс или интерфейс, указывая его каноническое имя (§6.7).
-
Объявление импорта всех типов пакета (§7.5.2) импортирует все доступные классы и интерфейсы указанного пакета, класса или интерфейса по мере необходимости, указывая каноническое имя пакета, класса или интерфейса.
-
Объявление импорта одного статического члена (§7.5.3) импортирует все доступные
staticчлены с заданным именем из класса или интерфейса, указав его каноническое имя. -
Объявление импорта всех статических членов (§7.5.4) импортирует все доступные
staticчлены указанного класса или интерфейса по мере необходимости, указав каноническое имя класса или интерфейса.
Область действия и перекрытие импортированного класса, интерфейса или члена указаны в §6.3 и §6.4.
Объявление import делает классы, интерфейсы или члены доступными по их простым именам только в единице компиляции, которая фактически содержит это объявление import. Область действия класса(ов), интерфейса(ов) или члена(ов), введенных объявлением import, специально не включает другие единицы компиляции в том же пакете, другие объявления import в текущей единице компиляции или объявление package в текущей единице компиляции (за исключением аннотаций объявления package).
Объявление импорта одного типа импортирует один класс или интерфейс, указав его каноническое имя, сделав его доступным под простым именем в модулях, классах и интерфейсах единицы компиляции, в которой это объявление присутствует.
TypeName должно быть каноническим именем класса или интерфейса (§6.7).
Класс или интерфейс должен быть членом именованного пакета или членом класса или интерфейса, чье внешнее лексически охватывающее объявление класса или интерфейса (§8.1.3) является членом именованного пакета, в противном случае возникает ошибка компиляции.
Если указанный класс или интерфейс недоступен (§6.6), возникает ошибка компиляции.
Если в одной единице компиляции два объявления импорта одного типа пытаются импортировать классы или интерфейсы с одинаковым простым именем, возникает ошибка компиляции, за исключением случаев, когда оба класса или интерфейса одинаковы, в этом случае дублирующее объявление игнорируется.
Если класс или интерфейс, импортируемый объявлением импорта одного типа, объявлен как верхнеуровневый класс или интерфейс (§7.6) в единице компиляции, содержащей объявление import, то объявление import игнорируется.
Если объявление импорта одного типа импортирует класс или интерфейс с простым именем x, а единица компиляции также объявляет верхнеуровневый класс или интерфейс с простым именем x, возникает ошибка компиляции.
Если единица компиляции содержит и объявление импорта одного типа, импортирующее класс или интерфейс с простым именем x, и объявление импорта одного статического члена (§7.5.3), импортирующее класс или интерфейс с простым именем x, возникает ошибка компиляции, за исключением случаев, когда оба класса или интерфейса одинаковы, в этом случае дублирующее объявление игнорируется.
Пример 7.5.1-1. Импорт одного типа
import java.util.Vector;
делает простое имя Vector доступным в объявлениях классов и интерфейсов в единице компиляции. Таким образом, простое имя Vector ссылается на объявление класса Vector в пакете java.util во всех местах, где оно не перекрывается (§6.4.1) или затемняется (§6.4.2) объявлением поля, параметра, локальной переменной или вложенного класса или интерфейса с тем же именем.
Обратите внимание, что фактическое объявление java.util.Vector является дженерическим (§8.1.2). После импорта имя Vector можно использовать без квалификации в параметризованном типе, таком как Vector<String>, или как сырой тип Vector. Соответствующее ограничение объявления import заключается в том, что вложенный класс или интерфейс, объявленный внутри дженерического класса или интерфейса, может быть импортирован, но его внешний тип всегда стирается.
Пример 7.5.1-2. Дублирование объявления класса
Эта программа:
import java.util.Vector;
class Vector { Object[] vec; }
вызывает ошибку компиляции из-за дублирования объявления Vector, как и:
import java.util.Vector; import myVector.Vector;
где myVector — пакет, содержащий единицу компиляции:
package myVector;
public class Vector { Object[] vec; }
Пример 7.5.1-3. Отсутствие импорта подпакета
Обратите внимание, что объявление import не может импортировать подпакет, только класс или интерфейс.
Например, попытка импортировать java.util и затем использовать имя util.Random для ссылки на тип java.util.Random не сработает:
import java.util;
class Test { util.Random generator; }
// incorrect: compile-time error
Пример 7.5.1-4. Импорт имени типа, которое также является именем пакета
Имена пакетов и имена типов обычно различаются согласно правилам именования, описанным в §6.1. Однако в искусственном примере, где есть нестандартно названный пакет Vector, который объявляет публичный класс с именем Mosquito:
package Vector;
public class Mosquito { int capacity; }
а затем единица компиляции:
package strange;
import java.util.Vector;
import Vector.Mosquito;
class Test {
public static void main(String[] args) {
System.out.println(new Vector().getClass());
System.out.println(new Mosquito().getClass());
}
}
объявление импорта класса Vector из пакета java.util не препятствует появлению и правильному распознаванию имени пакета Vector в последующих объявлениях import. Пример компилируется и выводит:
class java.util.Vector class Vector.Mosquito
Объявление импорта типов по требованию позволяет импортировать все доступные классы и интерфейсы заданного пакета, класса или интерфейса по мере необходимости.
ИмяПакетИлиТип должен представлять собой каноническое имя (§6.7) пакета, класса или интерфейса.
Если ИмяПакетИлиТип обозначает класс или интерфейс (§6.5.4), то класс или интерфейс должен быть членом именованного пакета, или членом класса или интерфейса, внешний лексически охватывающий класс или интерфейс объявления (§8.1.3) является членом именованного пакета, в противном случае возникает ошибка компиляции.
Если именованный пакет не виден текущему модулю (§7.4.3), или именованный класс или интерфейс недоступен (§6.6), то возникает ошибка компиляции.
Не является ошибкой компиляции именование либо java.lang, либо именованного пакета текущей единицы компиляции в объявлении импорта типов по требованию. В таких случаях объявление импорта типов по требованию игнорируется.
В одной единице компиляции может быть два или более объявлений импорта типов по требованию, имеющих одинаковый пакет, класс или интерфейс. Все, кроме одного, из этих объявлений считаются избыточными; эффект такой, как если бы этот тип был импортирован только один раз.
Если единица компиляции содержит как объявление импорта типов по требованию, так и объявление статического импорта по требованию (§7.5.4), которые имеют одинаковый класс или интерфейс, то эффект такой, как если бы static члены-классы и члены-интерфейсы этого класса или интерфейса (§8.5, §9.5) были импортированы только один раз.
Пример 7.5.2-1. Импорт типов по требованию
import java.util.*;
приводит к тому, что простые имена всех public классов и интерфейсов, объявленных в пакете java.util, становятся доступными внутри объявлений класса и интерфейса единицы компиляции. Таким образом, простое имя Vector относится к классу Vector пакета java.util во всех местах единицы компиляции, где это объявление класса не затеняется (§6.4.1) или скрывается (§6.4.2).
Это объявление может быть затенено объявлением импорта одного типа класса или интерфейса, простое имя которого Vector; классом или интерфейсом, именованным Vector и объявленным в пакете, к которому принадлежит единица компиляции; или любыми вложенными классами или интерфейсами.
Это объявление может быть скрыто объявлением поля, параметра или локальной переменной, именованной Vector.
(Встретить такие условия — большая редкость.)
Объявление одиночного статического импорта импортирует все доступные static члены с заданным простым именем из класса или интерфейса. Это делает эти static члены доступными под их простым именем в модуле, классе и интерфейсах единицы компиляции, в которой появляется объявление одиночного статического импорта.
ИмяТип должен представлять собой каноническое имя (§6.7) класса или интерфейса.
Класс или интерфейс должен быть членом именованного пакета, или членом класса или интерфейса, внешний лексически охватывающий класс или интерфейс объявления (§8.1.3) является членом именованного пакета, в противном случае возникает ошибка компиляции.
Если именованный класс или интерфейс недоступен (§6.6), то возникает ошибка компиляции.
Идентификатор должен называть по крайней мере одного static члена именованного класса или интерфейса. Если нет static члена с таким именем, или все именованные члены недоступны, возникает ошибка компиляции.
Допустимо, что одно объявление одиночного статического импорта импортирует несколько полей, классов или интерфейсов с одинаковым именем или несколько методов с одинаковым именем и сигнатурой. Это происходит, когда именованный класс или интерфейс наследует несколько полей, вложенных классов, вложенных интерфейсов или методов, все с одинаковым именем, от своих супертипов.
Если две объявления одиночного статического импорта в одной единице компиляции пытаются импортировать классы или интерфейсы с одинаковым простым именем, то возникает ошибка компиляции, если только два класса или интерфейса не являются одинаковыми, в этом случае дублирующее объявление игнорируется.
Если объявление одиночного статического импорта импортирует класс или интерфейс, простое имя которого x, а единица компиляции также объявляет класс или интерфейс верхнего уровня (§7.6), простое имя которого x, возникает ошибка компиляции.
Если единица компиляции содержит как объявление одиночного статического импорта, импортирующего класс или интерфейс, простое имя которого x, так и объявление одиночного импорта типа (§7.5.1), импортирующего класс или интерфейс, простое имя которого x, то возникает ошибка компиляции, если только два класса или интерфейса не являются одинаковыми, в этом случае дублирующее объявление игнорируется.
Объявление статического импорта по требованию позволяет импортировать все доступные static члены именованного класса или интерфейса по мере необходимости.
ИмяТип должен представлять собой каноническое имя (§6.7) класса или интерфейса.
Класс или интерфейс должен быть членом именованного пакета, или членом класса или интерфейса, внешний лексически охватывающий класс или интерфейс объявления (§8.1.3) является членом именованного пакета, в противном случае возникает ошибка компиляции.
Если именованный класс или интерфейс недоступен (§6.6), то возникает ошибка компиляции.
Два или более объявлений статического импорта по требованию в одной единице компиляции могут именовать один и тот же класс или интерфейс; эффект такой, как если бы было ровно одно такое объявление.
Два или более объявлений статического импорта по требованию в одной единице компиляции могут именовать один и тот же член; эффект такой, как если бы член был импортирован ровно один раз.
Допустимо, что одно объявление статического импорта по требованию импортирует несколько полей, классов или интерфейсов с одинаковым именем, или несколько методов с одинаковым именем и сигнатурой. Это происходит, когда именованный класс или интерфейс наследует несколько полей, вложенных классов, вложенных интерфейсов или методов, все с одинаковым именем, от своих супертипов.
Если единица компиляции содержит как объявление статического импорта по требованию, так и объявление импорта типов по требованию (§7.5.2), которые имеют одинаковый класс или интерфейс, то эффект такой, как если бы static члены-классы и члены-интерфейсы этого класса или интерфейса (§8.5, §9.5) были импортированы только один раз.
Объявление класса или интерфейса верхнего уровня объявляет класс верхнего уровня (§8.1) или интерфейс верхнего уровня (§9.1).
Дополнительные токены ";", появляющиеся на уровне объявлений классов и интерфейсов в единице компиляции, не влияют на значение единицы компиляции. В языке программирования Java допускаются лишние точки с запятой исключительно как уступка программистам C++, привыкшим к размещению ";" после объявления класса. Их не следует использовать в новом коде Java.
При отсутствии модификатора доступа класс или интерфейс верхнего уровня имеют доступ пакета: к нему можно получить доступ только внутри обычных единиц компиляции пакета, в котором он объявлен (§6.6.1). Класс или интерфейс могут быть объявлены public, чтобы предоставить доступ к классу или интерфейсу из кода в других пакетах того же модуля и, возможно, из кода в пакетах других модулей.
Ошибка времени компиляции, если объявление класса или интерфейса верхнего уровня содержит любой из следующих модификаторов доступа: protected, private или static.
Ошибка времени компиляции, если имя класса или интерфейса верхнего уровня появляется как имя другого класса или интерфейса верхнего уровня, объявленного в том же пакете.
Область действия и перекрытие класса или интерфейса верхнего уровня указаны в §6.3 и §6.4.
Полное имя класса или интерфейса верхнего уровня указано в §6.7.
Пример 7.6-1. Конфликтующие объявления класса и интерфейса верхнего уровня
package test;
import java.util.Vector;
class Point {
int x, y;
}
interface Point { // compile-time error #1
int getR();
int getTheta();
}
class Vector { Point[] pts; } // compile-time error #2
Здесь первая ошибка времени компиляции вызвана дублированным объявлением имени Point как класса, так и интерфейса в одном пакете. Вторая ошибка времени компиляции — попытка объявить имя Vector как классом, так и объявлением импорта типа.
Однако важно отметить, что это не ошибка, если имя в объявлении класса совпадает с классом или интерфейсом, которые иначе могли бы импортироваться объявлением импорта типа по умолчанию (§7.5.2) в той же единице компиляции. Таким образом, в этой программе:
package test;
import java.util.*;
class Vector {} // not a compile-time error
объявление класса Vector разрешено, даже если есть также класс java.util.Vector. Внутри этой единицы компиляции простое имя Vector относится к классу test.Vector, а не к классу java.util.Vector (к которому все же можно получить доступ внутри единицы компиляции, но только по его полному имени).
Пример 7.6-2. Область действия классов и интерфейсов верхнего уровня
package points;
class Point {
int x, y; // coordinates
PointColor color; // color of this point
Point next; // next point with this color
static int nPoints;
}
class PointColor {
Point first; // first point with this color
PointColor(int color) { this.color = color; }
private int color; // color components
}
Эта программа определяет два класса, которые используют друг друга в объявлениях их членов класса. Поскольку у классов Point и PointColor в качестве области действия объявлены все объявления классов в пакете points, включая все в текущей единице компиляции, эта программа компилируется без ошибок. То есть, ссылка вперёд не является проблемой.
Пример 7.6-3. Полные имена
class Point { int x, y; }
В этом коде класс Point объявлен в единице компиляции без объявления package, и, следовательно, Point является его полным именем, тогда как в коде:
package vista;
class Point { int x, y; }
полным именем класса Point является vista.Point. (Имя пакета vista подходит для локального или личного использования; если пакет предназначен для широкого распространения, лучше использовать уникальное имя пакета (§6.1).)
Реализация платформы Java SE должна отслеживать классы и интерфейсы внутри пакетов по сочетанию имен их окружающих модулей и их бинарных имён (§13.1). Несколько способов именования класса или интерфейса должны быть расширены до бинарных имён, чтобы убедиться, что такие имена понимаются как ссылки на один и тот же класс или интерфейс.
Например, если единица компиляции содержит объявление импорта одного типа (§7.5.1):
import java.util.Vector;
тогда в этой единице компиляции простое имя Vector и полное имя java.util.Vector ссылаются на один и тот же класс.
Если и только если пакеты хранятся в файловой системе (§7.2), система может выбрать применение ограничения, что ошибка времени компиляции, если класс или интерфейс не найден в файле под именем, составленным из имени класса или интерфейса плюс расширение (такое как .java или .jav), если верно одно из следующих утверждений:
-
К классу или интерфейсу обращается код в других обычных единицах компиляции пакета, в котором класс или интерфейс объявлен.
-
Класс или интерфейс объявлен
public(и поэтому потенциально доступен из кода в других пакетах).
Это ограничение подразумевает, что должно быть не более одного такого класса или интерфейса на единицу компиляции. Это ограничение облегчает поиск компилятору Java именованного класса или интерфейса в пакете. На практике многие программисты предпочитают помещать каждый класс или интерфейс в свою собственную единицу компиляции, независимо от того, является ли он public или ссылается на него код в других единицах компиляции.
Например, исходный код класса public wet.sprocket.Toad будет найден в файле Toad.java в каталоге wet/sprocket, а соответствующий объектный код будет найден в файле Toad.class в том же каталоге.
Объявление модуля определяет новый именованный модуль. Именованный модуль указывает зависимости от других модулей, чтобы определить множество классов и интерфейсов, доступных для его собственного кода; и указывает, какие из его пакетов экспортируются или открываются для заполнения множества классов и интерфейсов, доступных для других модулей, которые указывают зависимость от него.
«Зависимость» — это то, что выражается директивой requires, независимо от того, существует ли модуль с именем, указанным в директиве. «Зависимость» — это наблюдаемый модуль, перечисленный при разрешении (как описано в спецификации пакета java.lang.module) для заданной директивы requires. Как правило, правила языка программирования Java больше интересуются зависимостями, чем зависимостями.
Объявление модуля вводит имя модуля, которое может использоваться в других объявлениях модулей для выражения взаимосвязей между модулями. Имя модуля состоит из одного или нескольких идентификаторов Java (§3.8), разделенных токенами ".".
Существует два типа модулей: обычные модули и открытые модули. Тип модуля определяет характер доступа к типам модуля и членам этих типов для кода вне модуля.
Обычный модуль без модификатора open предоставляет доступ во время компиляции и выполнения только к типам в тех пакетах, которые явно экспортированы.
Открытый модуль с модификатором open предоставляет доступ во время компиляции только к типам в тех пакетах, которые явно экспортированы, но предоставляет доступ во время выполнения к типам во всех его пакетах, как если бы все пакеты были экспортированы.
Для кода вне модуля (независимо от того, является ли модуль обычным или открытым), доступ, предоставляемый во время компиляции или выполнения к типам в экспортированных пакетах модуля, относится конкретно к public и protected типам в этих пакетах, и к public и protected членам этих типов (§6.6). Доступ во время компиляции или выполнения к типам или их членам в пакетах, которые не экспортированы, не предоставляется. Код внутри модуля может получить доступ ко всем public и protected типам и к public и protected членам этих типов во всех пакетах модуля во время компиляции и выполнения.
В отличие от доступа во время компиляции и доступа во время выполнения, платформа Java SE предоставляет отраженный доступ через API Core Reflection (§1.4). Обычный модуль предоставляет отраженный доступ к типам только в тех пакетах, которые явно экспортированы или явно открыты (или оба). Открытый модуль предоставляет отраженный доступ к типам во всех своих пакетах, как если бы все пакеты были открыты.
Для кода вне обычного модуля предоставленный отраженный доступ к типам в экспортированных (и не открытых) пакетах модуля относится конкретно к public и protected типам в этих пакетах, и к public и protected членам этих типов. Отраженный доступ к типам в открытых пакетах модуля (независимо от того, экспортированы они или нет) предоставляется ко всем типам в этих пакетах и всем членам этих типов. Отраженный доступ к типам или их членам в пакетах, которые не экспортированы или не открыты, не предоставляется. Код внутри модуля имеет отраженный доступ ко всем типам и всем их членам во всех пакетах модуля.
Для кода вне открытого модуля отраженный доступ, предоставляемый к типам в открытых пакетах модуля (то есть, ко всем пакетам модуля), предоставляется ко всем типам в этих пакетах и всем членам этих типов. Код внутри модуля имеет отраженный доступ ко всем типам и всем их членам во всех пакетах модуля.
Директивы объявления модуля определяют зависимости модуля от других модулей (через requires, §7.7.1), пакеты, которые он предоставляет другим модулям (через exports и opens, §7.7.2), сервисы, которые он потребляет (через uses, §7.7.3), и сервисы, которые он предоставляет (через provides, §7.7.4).
requires {RequiresModifier} ModuleName ; exports PackageName [to ModuleName {, ModuleName}] ; opens PackageName [to ModuleName {, ModuleName}] ; uses TypeName ; provides TypeName with TypeName {, TypeName} ; transitive static Если и только если пакеты хранятся в файловой системе (§7.2), система может выбрать ограничение, что это ошибка во время компиляции, если объявление модуля не найдено в файле под именем, составленным из module-info плюс расширение (например, .java или .jav).
Для лучшего понимания принято, но не обязательно, чтобы объявление модуля группировало свои директивы так, чтобы директивы requires, относящиеся к модулям, были визуально отличны от директив exports и opens, относящихся к пакетам, и от директив uses и provides, относящихся к сервисам. Например:
module com.example.foo {
requires com.example.foo.http;
requires java.logging;
requires transitive com.example.foo.network;
exports com.example.foo.bar;
exports com.example.foo.internal to com.example.foo.probe;
opens com.example.foo.quux;
opens com.example.foo.internal to com.example.foo.network,
com.example.foo.probe;
uses com.example.foo.spi.Intf;
provides com.example.foo.spi.Intf with com.example.foo.Impl;
}
Директивы opens можно избежать, если модуль открыт:
open module com.example.foo {
requires com.example.foo.http;
requires java.logging;
requires transitive com.example.foo.network;
exports com.example.foo.bar;
exports com.example.foo.internal to com.example.foo.probe;
uses com.example.foo.spi.Intf;
provides com.example.foo.spi.Intf with com.example.foo.Impl;
}
Инструменты разработки для языка программирования Java рекомендуются для выделения requires transitive директив и неопределенных директив exports, поскольку они образуют основной API модуля.
Директива requires указывает имя модуля, от которого текущий модуль зависит.
Директива requires не должна появляться в объявлении модуля java.base, иначе произойдёт ошибка времени компиляции, поскольку это первичный модуль и у него нет зависимостей (§8.1.4).
Если объявление модуля не выражает зависимость от модуля java.base, и сам модуль не является java.base, то модуль неявно объявляет зависимость от модуля java.base.
Ключевое слово requires может быть дополнено модификатором transitive. Это заставляет любой модуль, который requires текущий модуль, иметь неявную зависимость от модуля, указанного в директиве requires transitive.
Ключевое слово requires может быть дополнено модификатором static. Это указывает, что зависимость, обязательная во время компиляции, является необязательной во время выполнения.
Если объявление модуля выражает зависимость от модуля java.base, и сам модуль не является java.base, то ошибка времени компиляции произойдёт, если после ключевого слова requires присутствует модификатор.
Ошибка времени компиляции произойдёт, если более одной директивы requires в объявлении модуля указывает на одно и то же имя модуля.
Ошибка времени компиляции произойдёт, если разрешение, как описано в спецификации пакета java.lang.module, с текущим модулем в качестве единственного корневого модуля, завершится неудачей по любой из причин, описанных в спецификации пакета java.lang.module.
Например, если директива requires указывает на невидимый модуль, или если текущий модуль напрямую или косвенно выражает зависимость от самого себя.
Если разрешение успешно выполняется, то его результат указывает модули, которые считываются текущим модулем. Модули, считываемые текущим модулем, определяют, какие обычные единицы компиляции видны текущему модулю (§7.3). Объявленные в этих обычных единицах компиляции типы (и только они) могут быть доступны коду в текущем модуле (§6.6).
Java SE Platform различает именованные модули, которые явно объявлены (то есть, с объявлением модуля), и именованные модули, которые неявно объявлены (то есть, автоматические модули). Однако, язык программирования Java не отображает это различие: директивы requires ссылаются на именованные модули без учёта того, явно они объявлены или неявно.
Хотя автоматические модули удобны для миграции, они ненадёжны в том смысле, что их имена и экспортируемые пакеты могут измениться, когда их авторы конвертируют их в явно объявленные модули. Компилятор Java рекомендуется выводить предупреждение, если директива requires ссылается на автоматический модуль. Особо сильное предупреждение рекомендуется, если в директиве присутствует модификатор transitive.
Пример 7.1.1-1. Разрешение директив requires transitive
Предположим, есть четыре объявления модулей следующим образом:
module m.A {
requires m.B;
}
module m.B {
requires transitive m.C;
}
module m.C {
requires transitive m.D;
}
module m.D {
exports p;
}
где пакет p, экспортируемый модулем m.D, объявлен следующим образом:
package p;
public class Point {}
и где пакет client в модуле m.A ссылается на тип public Point в экспортируемом пакете p:
package client;
import p.Point;
public class Test {
public static void main(String[] args) {
System.out.println(new Point());
}
}
Модули могут быть скомпилированы следующим образом, предполагая, что текущая директория имеет одну поддиректорию на модуль, названную в соответствии с модулем, который она содержит:
javac --module-source-path . -d . --module m.D javac --module-source-path . -d . --module m.C javac --module-source-path . -d . --module m.B javac --module-source-path . -d . --module m.A
Программа client.Test может быть запущена следующим образом:
java --module-path . --module m.A/client.Test
Ссылка из кода в m.A на экспортируемый тип public Point в m.D является корректной, потому что m.A считывает m.D, и m.D экспортирует пакет, содержащий Point. Разрешение определяет, что m.A считывает m.D следующим образом:
-
m.Arequiresm.Bи поэтому считываетm.B. -
Поскольку
m.Aсчитываетm.B, и посколькуm.Brequirestransitivem.C, разрешение определяет, чтоm.Aсчитываетm.C. -
Затем, поскольку
m.Aсчитываетm.C, и посколькуm.Crequirestransitivem.D, разрешение определяет, чтоm.Aсчитываетm.D.
По сути, модуль может считывать другой модуль через несколько уровней зависимости, чтобы поддерживать произвольное количество рефакторинга. После того, как модуль выпущен для повторного использования (через requires), автор модуля обязуется к его имени и API, но свободен рефакторить его содержимое в другие модули, которые изначальный модуль использует (через requires transitive) для пользы потребителей. В примере выше, пакет p изначально мог быть экспортирован модулем m.B (следовательно, m.A requires m.B), но рефакторинг привёл к перемещению части содержимого m.B в модули m.C и m.D. Используя цепочку директив requires transitive, семья m.B, m.C и m.D могут сохранить доступ к пакету p для кода в m.A без принуждения к изменениям директив requires в модуле m.A. Обратите внимание, что пакет p в m.D не "переэкспортируется" модулями m.C и m.B; вместо этого m.A заставляет считывать m.D напрямую.
Директива exports указывает имя пакета, который должен быть экспортирован текущим модулем. Для кода в других модулях это предоставляет доступ во время компиляции и выполнения к типам public и protected в пакете, и к членам public и protected этих типов (§6.6). Это также предоставляет рефлексивный доступ к этим типам и членам для кода в других модулях.
Директива opens указывает имя пакета, который должен быть открыт текущим модулем. Для кода в других модулях это предоставляет доступ во время выполнения, но не во время компиляции, к типам public и protected в пакете, и к членам public и protected этих типов. Это также предоставляет рефлексивный доступ ко всем типам в пакете и всем их членам для кода в других модулях.
Ошибка времени компиляции произойдёт, если пакет, указанный в директиве exports, не объявлен единицей компиляции, связанной с текущим модулем (§7.3).
Разрешено указать в директиве opens пакет, который не объявлен единицей компиляции, связанной с текущим модулем. (Если пакет объявлен наблюдаемой единицей компиляции, связанной с другим модулем, директива opens не оказывает никакого эффекта на этот другой модуль.)
Ошибка времени компиляции произойдёт, если более одной директивы exports в объявлении модуля указывает на одно и то же имя пакета.
Ошибка времени компиляции произойдёт, если более одной директивы opens в объявлении модуля указывает на одно и то же имя пакета.
Ошибка времени компиляции произойдёт, если директива opens появляется в объявлении открытого модуля.
Если директива exports или opens имеет клаузу to, то директива является квалифицированной; в противном случае — неквалифицированной. Для квалифицированной директивы типы public и protected в пакете, и их члены public и protected, доступны только коду в модулях, указанных в клаузе to. Модули, указанные в клаузе to, называются друзьями текущего модуля. Для неквалифицированной директивы эти типы и их члены доступны коду в любом модуле.
Разрешено, чтобы клауза to в директиве exports или opens указала на модуль, который не является наблюдаемым (§7.7.6).
Ошибка времени компиляции произойдёт, если клауза to данной директивы exports указывает одно и то же имя модуля более одного раза.
Ошибка времени компиляции произойдёт, если клауза to данной директивы opens указывает одно и то же имя модуля более одного раза.
Директива uses указывает на сервис, для которого код в текущем модуле может обнаружить поставщиков посредством java.util.ServiceLoader.
Это ошибка времени компиляции, если директива uses указывает на класс перечислений (§8.9).
Сервис может быть объявлен в текущем модуле или в другом модуле. Если сервис не объявлен в текущем модуле, то сервис должен быть доступен для кода в текущем модуле (§6.6), иначе произойдёт ошибка времени компиляции.
Это ошибка времени компиляции, если более одной директивы uses в объявлении модуля указывает на один и тот же сервис.
Директива provides указывает на сервис, для которого в разделе with указан один или несколько поставщиков сервиса для java.util.ServiceLoader.
Это ошибка времени компиляции, если директива provides указывает на класс перечислений (§8.9) как сервис.
Сервис может быть объявлен в текущем модуле или в другом модуле. Если сервис не объявлен в текущем модуле, то сервис должен быть доступен для кода в текущем модуле (§6.6), иначе произойдёт ошибка времени компиляции.
Каждый поставщик сервиса должен быть классом или интерфейсом public, который является либо верхнего уровня, либо static, иначе произойдёт ошибка времени компиляции.
Каждый поставщик сервиса должен быть объявлен в текущем модуле, иначе произойдёт ошибка времени компиляции.
Если поставщик сервиса явно объявляет конструктор public без формальных параметров или неявно объявляет конструктор по умолчанию public (§8.8.9), то этот конструктор называется конструктором поставщика.
Если поставщик сервиса явно объявляет метод public static, называемый provider, без формальных параметров, то этот метод называется методом поставщика.
Если у поставщика сервиса есть метод поставщика, то его тип возвращаемого значения должен (i) либо быть объявлен в текущем модуле, либо быть объявлен в другом модуле и быть доступен коду в текущем модуле; и (ii) быть подтипом сервиса, указанного в директиве provides; иначе произойдёт ошибка времени компиляции.
Хотя поставщик сервиса, указанный в директиве provides, должен быть объявлен в текущем модуле, его метод поставщика может иметь тип возвращаемого значения, объявленный в другом модуле. Также обратите внимание, что когда поставщик сервиса объявляет метод поставщика, сам поставщик сервиса не обязательно должен быть подтипом сервиса.
Если у поставщика сервиса нет метода поставщика, то этот поставщик сервиса должен иметь конструктор поставщика и должен быть подтипом сервиса, указанного в директиве provides; иначе произойдёт ошибка времени компиляции.
Это ошибка времени компиляции, если более одной директивы provides в объявлении модуля указывает на один и тот же сервис.
Это ошибка времени компиляции, если раздел with данной директивы provides указывает на один и тот же поставщика сервиса более одного раза.
Наблюдаемый обычный блок компиляции, который системный хост не связывает с именованным модулем (§7.3), связывается с безымянным модулем.
Безымянные модули предоставляются платформой Java SE в связи с тем, что программы, разработанные до Java SE 9, не могли объявлять именованные модули. Кроме того, причины, по которым платформа Java SE предоставляет безымянные пакеты (§7.4.2), в значительной степени применимы к безымянным модулям.
Реализация платформы Java SE должна поддерживать как минимум один безымянный модуль. Реализация может поддерживать более одного безымянного модуля, но это не является обязательным. Какие обычные блоки компиляции связаны с каждым безымянным модулем, определяется системным хостом.
Системный хост может связать блоки обычной компиляции в именованном пакете с безымянным модулем.
Правила для безымянных модулей разработаны таким образом, чтобы максимально увеличить их взаимодействие с именованными модулями, а именно:
-
Безымянный модуль читает каждый наблюдаемый модуль (§7.7.6).
Поскольку обычный блок компиляции, связанный с безымянным модулем, является наблюдаемым, сам безымянный модуль является наблюдаемым. Таким образом, если реализация платформы Java SE поддерживает более одного безымянного модуля, каждый безымянный модуль является наблюдаемым, и каждый безымянный модуль читает каждый безымянный модуль, включая себя.
Однако важно понимать, что блоки обычной компиляции безымянного модуля никогда не видимы для именованного модуля (§7.3), потому что ни одна директива
requiresне может организовать чтение именованного модуля безымянным модулем. API ядра рефлексии платформы Java SE может использоваться для организации чтения безымянного модуля именованным модулем во время выполнения. -
Безымянный модуль экспортирует каждый пакет, блоки обычной компиляции которого связаны с этим безымянным модулем.
-
Безымянный модуль открывает каждый пакет, блоки обычной компиляции которого связаны с этим безымянным модулем.
Модуль является наблюдаемым, если выполняется хотя бы одно из следующих условий:
-
Модульный блок компиляции, содержащий объявление модуля, является наблюдаемым (§7.3).
-
Обычный блок компиляции, связанный с модулем, является наблюдаемым.
© Oracle and/or its affiliates. All rights reserved.
Licensed under the Oracle Technology Network License Agreement.