Spec-Zone.ru › Java Language Specification 24

Глава 7. Пакеты и модули

Оглавление

7.1. Члены пакета
7.2. Поддержка пакетов и модулей хостом
7.3. Единицы компиляции
7.4. Объявления пакетов
7.4.1. Именные пакеты
7.4.2. Без имени пакеты
7.4.3. Наблюдение и видимость пакетов
7.5. Объявления импорта
7.5.1. Объявления импорта одного типа
7.5.2. Объявления импорта типов по требованию
7.5.3. Объявления импорта одного статического элемента
7.5.4. Объявления импорта статических элементов по требованию
7.6. Объявления верхнего уровня классов и интерфейсов
7.7. Объявления модулей
7.7.1. Зависимости
7.7.2. Экспортированные и открытые пакеты
7.7.3. Потребление сервисов
7.7.4. Предоставление сервисов
7.7.5. Модули без имени
7.7.6. Наблюдаемость модуля

Программы организованы как наборы пакетов. Членами пакета (§7.1) являются классы и интерфейсы, которые объявлены в единицах компиляции пакета, и подпакеты, которые могут содержать единицы компиляции и подпакеты.

Каждый пакет имеет свой собственный набор имён для классов и интерфейсов, что помогает предотвратить конфликты имён. Структура имён для пакетов иерархическая.

Если набор пакетов достаточно сплочён, то пакеты могут быть сгруппированы в модуль. Модуль классифицирует некоторые или все свои пакеты как экспортированные, что означает, что к их классам и интерфейсам можно получить доступ из кода вне модуля. Если пакет не экспортирован модулем, то только код внутри модуля может получить доступ к его классам и интерфейсам. Кроме того, если код в модуле хочет получить доступ к пакетам, экспортированным другим модулем, то первый модуль должен явно зависеть от второго модуля. Таким образом, модуль управляет тем, как его пакеты используют другие модули (указанием зависимостей) и управляет тем, как другие модули используют его пакеты (указанием, какие пакеты экспортированы).

Модули и пакеты могут храниться в файловой системе или в базе данных (§7.2). Модули и пакеты, хранящиеся в файловой системе, могут иметь определённые ограничения на организацию своих единиц компиляции, чтобы позволить простой реализации легко находить объявления модулей, классов и интерфейсов.

Код в единице компиляции автоматически имеет доступ ко всем классам и интерфейсам, объявленным в его пакете, а также автоматически импортирует все public классы и интерфейсы, объявленные в предопределённом пакете java.lang.

Класс или интерфейс верхнего уровня доступен (§6.6) вне пакета, который его объявляет, только если класс или интерфейс объявлен public. Класс или интерфейс верхнего уровня доступен вне модуля, который его объявляет, только если класс или интерфейс объявлен public и является членом экспортированного пакета. Класс или интерфейс, который объявлен public, но не является членом экспортированного пакета, доступен только коду внутри модуля.

Для небольших программ и обычной разработки пакет может быть безымянным (§7.4.2) или иметь простое имя, но если код должен быть широко распространён, следует выбирать уникальные имена пакетов, используя полные имена. Это может предотвратить конфликты, которые могли бы возникнуть, если бы две группы разработчиков выбрали одно и то же имя пакета, и эти пакеты впоследствии были бы использованы в одной программе.

7.1. Члены пакета

Членами пакета являются его подпакеты и все классы верхнего уровня (§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.2. Поддержка хоста для модулей и пакетов

Каждая система хоста определяет, как создаются и хранятся модули, пакеты и единицы компиляции.

Каждая система хоста определяет, какие единицы компиляции наблюдаемы в конкретной компиляции (§7.3). Каждая система хоста также определяет, какие наблюдаемые единицы компиляции связаны с модулем. Наблюдаемость единиц компиляции, связанных с модулем, определяет, какие модули наблюдаемы (§7.7.6) и какие пакеты видны внутри этих модулей (§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

Если символ @ не является допустимым символом в имени файла для данной файловой системы хоста, то вместо него может быть использован другой символ, который недопустим в идентификаторе.

7.3. Единицы компиляции

CompilationUnit — это целевой символ (§2.1) для синтаксической грамматики (§2.3) программ Java. Он определяется следующей продукцией:

CompilationUnit:
OrdinaryCompilationUnit
ModularCompilationUnit
OrdinaryCompilationUnit:
[PackageDeclaration] {ImportDeclaration} {TopLevelClassOrInterfaceDeclaration}
ModularCompilationUnit:
{ImportDeclaration} ModuleDeclaration

Обычная единица компиляции состоит из трёх частей, каждая из которых является необязательной:

  • Объявление 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 должен организовать компиляцию всех таких классов и интерфейсов одновременно.

7.4. Объявления пакетов

Объявление пакета появляется внутри обычного модуля компиляции, чтобы указать пакет, к которому принадлежит модуль компиляции.

7.4.1. Имя пакета

Объявление пакета в обычном модуле компиляции указывает имя (§6.2) пакета, к которому принадлежит модуль компиляции.

PackageDeclaration:
{Модификатор пакета} package Идентификатор {. Идентификатор} ;
PackageModifier:
Аннотация

Имя пакета, упомянутое в объявлении пакета, должно быть полным квалифицированным именем пакета (§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 становится единственным хранилищем аннотаций и документации на уровне пакета. Если в будущем потребуется добавить любую другую информацию на уровне пакета, этот файл должен оказаться удобным местом для этой информации.

7.4.2. Пакеты без имени

Обычный модуль компиляции, не имеющий объявления пакета, но имеющий по крайней мере один другой тип объявления, является частью пакета без имени.

Пакеты без имени предоставляются платформой 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.4.3. Наблюдаемость и видимость пакетов

Пакет наблюдаем тогда и только тогда, когда выполняется хотя бы одно из следующих условий:

  • Обычный модуль компиляции, содержащий объявление пакета, наблюдаем (§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.

7.5. Объявления импорта

Объявление импорта позволяет ссылаться на именованный класс, интерфейс или static член с помощью простого имени (§6.2), состоящего из одного идентификатора.

Без соответствующего объявления импорта ссылка на класс или интерфейс, объявленный в другом пакете, или на static член другого класса или интерфейса, обычно должна использовать полное имя (§6.7).

ImportDeclaration:
SingleTypeImportDeclaration
TypeImportOnDemandDeclaration
SingleStaticImportDeclaration
StaticImportOnDemandDeclaration
  • Объявление импорта одного типа (§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).

7.5.1. Объявления импорта одного типа

Объявление импорта одного типа импортирует один класс или интерфейс, указав его каноническое имя, сделав его доступным под простым именем в модулях, классах и интерфейсах единицы компиляции, в которой это объявление присутствует.

SingleTypeImportDeclaration:
import TypeName ;

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

7.5.2. Объявления импорта типов по требованию

Объявление импорта типов по требованию позволяет импортировать все доступные классы и интерфейсы заданного пакета, класса или интерфейса по мере необходимости.

TypeImportOnDemandDeclaration:
import ИмяПакетИлиТип . * ;

ИмяПакетИлиТип должен представлять собой каноническое имя (§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.

(Встретить такие условия — большая редкость.)


7.5.3. Объявления одиночного статического импорта

Объявление одиночного статического импорта импортирует все доступные static члены с заданным простым именем из класса или интерфейса. Это делает эти static члены доступными под их простым именем в модуле, классе и интерфейсах единицы компиляции, в которой появляется объявление одиночного статического импорта.

SingleStaticImportDeclaration:
import static ИмяТип . Идентификатор ;

ИмяТип должен представлять собой каноническое имя (§6.7) класса или интерфейса.

Класс или интерфейс должен быть членом именованного пакета, или членом класса или интерфейса, внешний лексически охватывающий класс или интерфейс объявления (§8.1.3) является членом именованного пакета, в противном случае возникает ошибка компиляции.

Если именованный класс или интерфейс недоступен (§6.6), то возникает ошибка компиляции.

Идентификатор должен называть по крайней мере одного static члена именованного класса или интерфейса. Если нет static члена с таким именем, или все именованные члены недоступны, возникает ошибка компиляции.

Допустимо, что одно объявление одиночного статического импорта импортирует несколько полей, классов или интерфейсов с одинаковым именем или несколько методов с одинаковым именем и сигнатурой. Это происходит, когда именованный класс или интерфейс наследует несколько полей, вложенных классов, вложенных интерфейсов или методов, все с одинаковым именем, от своих супертипов.

Если две объявления одиночного статического импорта в одной единице компиляции пытаются импортировать классы или интерфейсы с одинаковым простым именем, то возникает ошибка компиляции, если только два класса или интерфейса не являются одинаковыми, в этом случае дублирующее объявление игнорируется.

Если объявление одиночного статического импорта импортирует класс или интерфейс, простое имя которого x, а единица компиляции также объявляет класс или интерфейс верхнего уровня (§7.6), простое имя которого x, возникает ошибка компиляции.

Если единица компиляции содержит как объявление одиночного статического импорта, импортирующего класс или интерфейс, простое имя которого x, так и объявление одиночного импорта типа (§7.5.1), импортирующего класс или интерфейс, простое имя которого x, то возникает ошибка компиляции, если только два класса или интерфейса не являются одинаковыми, в этом случае дублирующее объявление игнорируется.

7.5.4. Объявления статического импорта по требованию

Объявление статического импорта по требованию позволяет импортировать все доступные static члены именованного класса или интерфейса по мере необходимости.

StaticImportOnDemandDeclaration:
import static ИмяТип . * ;

ИмяТип должен представлять собой каноническое имя (§6.7) класса или интерфейса.

Класс или интерфейс должен быть членом именованного пакета, или членом класса или интерфейса, внешний лексически охватывающий класс или интерфейс объявления (§8.1.3) является членом именованного пакета, в противном случае возникает ошибка компиляции.

Если именованный класс или интерфейс недоступен (§6.6), то возникает ошибка компиляции.

Два или более объявлений статического импорта по требованию в одной единице компиляции могут именовать один и тот же класс или интерфейс; эффект такой, как если бы было ровно одно такое объявление.

Два или более объявлений статического импорта по требованию в одной единице компиляции могут именовать один и тот же член; эффект такой, как если бы член был импортирован ровно один раз.

Допустимо, что одно объявление статического импорта по требованию импортирует несколько полей, классов или интерфейсов с одинаковым именем, или несколько методов с одинаковым именем и сигнатурой. Это происходит, когда именованный класс или интерфейс наследует несколько полей, вложенных классов, вложенных интерфейсов или методов, все с одинаковым именем, от своих супертипов.

Если единица компиляции содержит как объявление статического импорта по требованию, так и объявление импорта типов по требованию (§7.5.2), которые имеют одинаковый класс или интерфейс, то эффект такой, как если бы static члены-классы и члены-интерфейсы этого класса или интерфейса (§8.5, §9.5) были импортированы только один раз.

7.6. Объявления верхнего уровня классов и интерфейсов

Объявление класса или интерфейса верхнего уровня объявляет класс верхнего уровня (§8.1) или интерфейс верхнего уровня (§9.1).

TopLevelClassOrInterfaceDeclaration:
ClassDeclaration
InterfaceDeclaration
;

Дополнительные токены ";", появляющиеся на уровне объявлений классов и интерфейсов в единице компиляции, не влияют на значение единицы компиляции. В языке программирования 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 в том же каталоге.

7.7. Объявления модулей

Объявление модуля определяет новый именованный модуль. Именованный модуль указывает зависимости от других модулей, чтобы определить множество классов и интерфейсов, доступных для его собственного кода; и указывает, какие из его пакетов экспортируются или открываются для заполнения множества классов и интерфейсов, доступных для других модулей, которые указывают зависимость от него.

«Зависимость» — это то, что выражается директивой requires, независимо от того, существует ли модуль с именем, указанным в директиве. «Зависимость» — это наблюдаемый модуль, перечисленный при разрешении (как описано в спецификации пакета java.lang.module) для заданной директивы requires. Как правило, правила языка программирования Java больше интересуются зависимостями, чем зависимостями.

ModuleDeclaration:
{Annotation} [open] module Identifier {. Identifier} { {ModuleDirective} }

Объявление модуля вводит имя модуля, которое может использоваться в других объявлениях модулей для выражения взаимосвязей между модулями. Имя модуля состоит из одного или нескольких идентификаторов 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).

ModuleDirective:
requires {RequiresModifier} ModuleName ;
exports PackageName [to ModuleName {, ModuleName}] ;
opens PackageName [to ModuleName {, ModuleName}] ;
uses TypeName ;
provides TypeName with TypeName {, TypeName} ;
RequiresModifier:
(one of)
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 модуля.

7.7.1. Зависимости

Директива requires указывает имя модуля, от которого текущий модуль зависит.

Директива requires не должна появляться в объявлении модуля java.base, иначе произойдёт ошибка времени компиляции, так как это исходный модуль и у него нет зависимостей (§8.1.4).

Если объявление модуля не выражает зависимость от модуля java.base, и сам модуль не является java.base, то модуль неявно зависит от модуля java.base.

Ключевое слово requires может быть дополнено модификатором transitive. Это вызывает неявную зависимость любого модуля, который requires текущий модуль, от модуля, указанного в директиве requires transitive.

Ключевое слово requires может быть дополнено модификатором static. Это означает, что зависимость, обязательная на стадии компиляции, является необязательной во время выполнения.

Ошибка времени компиляции, если один и тот же модификатор появляется более одного раза после ключевого слова requires.

Если объявление модуля выражает зависимость от модуля java.base, и сам модуль не является java.base, то ошибкой времени компиляции является наличие модификатора после ключевого слова requires.

Ошибка времени компиляции, если более одной директивы requires в объявлении модуля указывает одно и то же имя модуля.

Ошибка времени компиляции, если разрешение, как описано в спецификации пакета java.lang.module, с текущим модулем в качестве единственного корневого модуля, завершается ошибкой по любой из причин, описанных в спецификации пакета java.lang.module.

Например, если директива requires указывает на неопределяемый модуль, или если текущий модуль непосредственно или косвенно выражает зависимость от самого себя.

Если разрешение успешно, то его результат определяет модули, которые считывает текущий модуль. Модули, считанные текущим модулем, определяют, какие обычные единицы компиляции видны текущему модулю (§7.3). Типы, объявленные в этих обычных единицах компиляции (и только в этих обычных единицах компиляции), могут быть доступны коду в текущем модуле (§6.6).

Платформа Java SE различает именованные модули, которые явно объявлены (то есть с помощью объявления модуля), и именованные модули, которые неявно объявлены (то есть автоматические модули). Однако язык программирования 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.A requires m.B и поэтому считывает m.B.

  • Поскольку m.A считывает m.B, и поскольку m.B requires transitive m.C, разрешение определяет, что m.A считывает m.C.

  • Затем, поскольку m.A считывает m.C, и поскольку m.C requires transitive m.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 напрямую.


7.7.2. Экспортированные и Открытые пакеты

Директива 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 указывает одно и то же имя модуля более одного раза.

7.7.3. Потребление сервиса

Директива uses указывает на сервис, для которого код в текущем модуле может обнаруживать поставщиков через java.util.ServiceLoader.

Ошибка времени компиляции, если директива uses указывает на класс перечислений (§8.9).

Сервис может быть объявлен в текущем модуле или в другом модуле. Если сервис не объявлен в текущем модуле, то он должен быть доступен коду текущего модуля (§6.6), иначе произойдёт ошибка времени компиляции.

Ошибка времени компиляции, если более одной директивы uses в объявлении модуля указывают на один и тот же сервис.

7.7.4. Предоставление сервиса

Директива 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.7.5. Безымянные модули

Наблюдаемый обычный модуль компиляции, которому системный хост не сопоставил имя модуля (§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.7.6. Наблюдаемость модуля

Модуль наблюдаем, если хотя бы одно из следующих условий истинно:

  • Модульный модуль компиляции, содержащий объявление модуля, наблюдаем (§7.3).

  • Обычный модуль компиляции, связанный с модулем, наблюдаем.

© Oracle and/or its affiliates. All rights reserved.
Licensed under the Oracle Technology Network License Agreement.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API