Spec-Zone.ru › OpenJDK 25

Класс MethodHandles.Lookup

java.lang.Object
java.lang.invoke.MethodHandles.Lookup
Внешний класс:
MethodHandles
public static final class MethodHandles.Lookup extends Object
Объект поиска — это фабрика для создания дескрипторов методов в случаях, когда при создании требуется проверка доступа. Проверки доступа не выполняются при вызове дескрипторов методов, а выполняются при их создании. Поэтому ограничения доступа к дескриптору метода должны обеспечиваться при его создании. Класс вызывающего кода, относительно которого обеспечиваются эти ограничения, называется классом поиска.

Класс поиска, которому необходимо создавать дескрипторы методов, вызывает MethodHandles.lookup, чтобы создать фабрику для себя. При создании объекта фабрики Lookup определяется идентификатор класса поиска, который безопасно сохраняется в объекте Lookup. Затем класс поиска (или его делегаты) может использовать фабричные методы объекта Lookup для создания дескрипторов методов для членов, доступ к которым проверяется. Сюда входят все методы, конструкторы и поля, доступные классу поиска, в том числе закрытые.

Фабричные методы поиска

Фабричные методы объекта Lookup соответствуют всем основным сценариям использования методов, конструкторов и полей. Каждый дескриптор метода, созданный фабричным методом, функционально эквивалентен определённому поведению байт-кода. (Поведение байт-кода описано в разделе 5.4.3.5 спецификации виртуальной машины Java.) Ниже приведена сводка соответствия между этими фабричными методами и поведением получаемых дескрипторов методов:
поведение методов поиска
выражение поиска член поведение байт-кода
lookup.findGetter(C.class,"f",FT.class) FT f; (FT) this.f;
lookup.findStaticGetter(C.class,"f",FT.class) static
FT f;
(FT) C.f;
lookup.findSetter(C.class,"f",FT.class) FT f; this.f = x;
lookup.findStaticSetter(C.class,"f",FT.class) static
FT f;
C.f = arg;
lookup.findVirtual(C.class,"m",MT) T m(A*); (T) this.m(arg*);
lookup.findStatic(C.class,"m",MT) static
T m(A*);
(T) C.m(arg*);
lookup.findSpecial(C.class,"m",MT,this.class) T m(A*); (T) super.m(arg*);
lookup.findConstructor(C.class,MT) C(A*); new C(arg*);
lookup.unreflectGetter(aField) (static)?
FT f;
(FT) aField.get(thisOrNull);
lookup.unreflectSetter(aField) (static)?
FT f;
aField.set(thisOrNull, arg);
lookup.unreflect(aMethod) (static)?
T m(A*);
(T) aMethod.invoke(thisOrNull, arg*);
lookup.unreflectConstructor(aConstructor) C(A*); (C) aConstructor.newInstance(arg*);
lookup.unreflectSpecial(aMethod,this.class) T m(A*); (T) super.m(arg*);
lookup.findClass("C") class C { ... } C.class;
Здесь тип C — это класс или интерфейс, в котором выполняется поиск члена; в методах поиска он документируется как параметр с именем refc. Тип метода MT состоит из типа возвращаемого значения T и последовательности типов аргументов A*. Конструктор также имеет последовательность типов аргументов A* и считается возвращающим вновь созданный объект типа C. И MT, и тип поля FT документируются как параметр с именем type. Формальный параметр this обозначает ссылку на самого себя типа C; если он присутствует, то всегда является первым аргументом вызова дескриптора метода. (Для некоторых членов protected тип this может быть ограничен классом поиска; см. ниже.) Имя arg обозначает все остальные аргументы дескриптора метода. В примерах кода для API Core Reflection имя thisOrNull обозначает ссылку null, если вызываемый метод или поле является статическим, и this в противном случае. Имена aMethod, aField и aConstructor обозначают отражательные объекты, соответствующие указанным членам, объявленным в типе C.

Поведение байт-кода для операции findClass — это загрузка константного класса, как при помощи ldc CONSTANT_Class. Это поведение представлено не дескриптором метода, а непосредственно константой Class.

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

Обсуждение: Эквивалентность найденных дескрипторов методов, соответствующих членов класса и поведений байт-кода может нарушаться в нескольких случаях:

  • Если C недоступен для символьного доступа из загрузчика класса поиска, поиск всё равно может завершиться успешно, даже если эквивалентного выражения Java или константы байт-кода не существует.
  • Аналогично, поиск всё равно может завершиться успешно, если T или MT недоступен для символьного доступа из загрузчика класса поиска. Например, поиск MethodHandle.invokeExact и MethodHandle.invoke всегда завершается успешно независимо от запрошенного типа.
  • Если у найденного метода очень большая арность, создание дескриптора метода может завершиться ошибкой IllegalArgumentException, поскольку тип дескриптора метода содержит слишком много параметров.

Проверка доступа

В фабричных методах Lookup проверка доступа выполняется при создании дескриптора метода. Это ключевое отличие от API Core Reflection, поскольку java.lang.reflect.Method.invoke выполняет проверку доступа для каждого вызывающего кода при каждом вызове.

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

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

Поиск может завершиться неудачно, если содержащий класс недоступен классу поиска, требуемый член класса отсутствует, требуемый член класса недоступен классу поиска или объект поиска не обладает достаточным уровнем доверия для доступа к члену. Для функции установки значения поля final обеспечение неизменяемости рассматривается как разновидность контроля доступа, поэтому поиск завершится неудачно, кроме особых случаев Lookup.unreflectSetter. В любом из этих случаев при попытке поиска будет выброшено исключение ReflectiveOperationException. Точный класс исключения будет одним из следующих:

  • NoSuchMethodException — если запрошен метод, который не существует
  • NoSuchFieldException — если запрошено поле, которое не существует
  • IllegalAccessException — если член существует, но проверка доступа не пройдена

В целом условия, при которых для метода M можно найти дескриптор метода, не строже условий, при которых класс поиска мог бы скомпилировать, проверить и разрешить вызов M. В случаях, когда JVM выбросила бы исключения, такие как NoSuchMethodError, поиск дескриптора метода обычно выбрасывает соответствующее проверяемое исключение, например NoSuchMethodException. А эффект вызова дескриптора метода, полученного в результате поиска, в точности эквивалентен выполнению скомпилированного, проверенного и разрешённого вызова M. То же справедливо для полей и конструкторов.

Обсуждение: Проверки доступа применяются только к именованным и отражённым методам, конструкторам и полям. Другие методы создания дескрипторов методов, такие как MethodHandle.asType, не требуют проверок доступа и используются независимо от объекта Lookup.

Если требуемый член является protected, применяются обычные правила JVM, включая требование, чтобы класс поиска находился в том же пакете, что и требуемый член, либо наследовал этот член. (См. разделы 4.9.2, 5.4.3.5 и 6.4 спецификации виртуальной машины Java.) Кроме того, если требуемый член является нестатическим полем или методом в другом пакете, полученный дескриптор метода можно применять только к объектам класса поиска или одного из его подклассов. Это требование обеспечивается сужением типа первого параметра this с C (который обязательно будет суперклассом класса поиска) до самого класса поиска.

JVM предъявляет аналогичное требование к инструкции invokespecial: аргумент-получатель должен соответствовать как разрешённому методу, так и текущему классу. И это требование обеспечивается сужением типа первого параметра полученного дескриптора метода. (См. раздел 4.10.1.9 спецификации виртуальной машины Java.)

JVM представляет конструкторы и блоки статической инициализации как внутренние методы со специальными именами ("<init>" и "<clinit>"). Внутренний синтаксис инструкций вызова позволяет ссылаться на такие внутренние методы, как на обычные методы, но верификатор байт-кода JVM их отклоняет. Поиск такого внутреннего метода приведёт к NoSuchMethodException.

Если связь между вложенными типами выражена непосредственно атрибутами NestHost и NestMembers (см. разделы 4.7.28 и 4.7.29 спецификации виртуальной машины Java), соответствующий объект Lookup обеспечивает прямой доступ к классу поиска и всем его членам гнезда (см. Class.getNestHost). В противном случае доступ между вложенными классами обеспечивается компилятором Java, который создаёт метод-обёртку для доступа к закрытому методу другого класса в том же гнезде. Например, вложенный класс C.D может обращаться к закрытым членам других связанных классов, таких как C, C.D.E или C.B, однако компилятору Java может потребоваться сгенерировать методы-обёртки в этих связанных классах. В таких случаях объект Lookup для класса C.E не сможет получить доступ к этим закрытым членам. Обойти это ограничение можно с помощью метода Lookup.in, который преобразует поиск для класса C.E в поиск для любого из этих других классов без специального повышения привилегий.

Доступ, предоставляемый определённому объекту поиска, может быть ограничен его набором lookupModes и охватывать лишь часть членов, обычно доступных классу поиска. Например, метод publicLookup возвращает объект поиска, которому разрешён доступ только к открытым членам открытых классов в экспортируемых пакетах. Метод с учётом вызывающего кода lookup возвращает объект поиска с полными возможностями относительно класса вызывающего кода, позволяющими эмулировать все поддерживаемые формы поведения байт-кода. Кроме того, метод Lookup.in может вернуть объект поиска с меньшим количеством режимов доступа, чем у исходного объекта поиска.

Обсуждение закрытого доступа и доступа к модулю: Мы говорим, что поиск обладает закрытым доступом, если его режимы поиска включают возможность доступа к членам private (включая закрытые члены классов того же гнезда). Как указано в описаниях соответствующих методов, только поиски с закрытым доступом обладают следующими возможностями:

  • доступ к закрытым полям, методам и конструкторам класса поиска и его членов гнезда
  • создание дескрипторов методов, выполняющих инструкции emulate invokespecial
  • создание объектов delegated lookup objects, обладающих закрытым доступом к другим классам того же члена пакета

Аналогично, поиск с доступом к модулю гарантирует, что исходный создатель поиска был членом того же модуля, что и класс поиска.

Закрытый доступ и доступ к модулю — это независимые режимы, определяемые отдельно; поиск может обладать одним из них, обоими или ни одним. Поиск, обладающий обоими режимами доступа, называется поиском с полным привилегированным доступом.

Исходный доступ гарантирует, что поиск создан исходным классом поиска и методом начальной загрузки, вызванным виртуальной машиной. Такой поиск с исходным доступом также обладает закрытым доступом и доступом к модулю, а также следующими дополнительными возможностями:

  • создание дескрипторов методов, вызывающих методы с учётом вызывающего кода, например Class.forName
  • получение данных класса, связанных с классом поиска

Каждое из этих разрешений обусловлено тем, что объект поиска с закрытым доступом можно безопасно проследить до исходного класса, поведение байт-кода и разрешения доступа которого, предоставленные языком Java, можно надёжно определить и эмулировать с помощью дескрипторов методов.

Поиск между модулями

Когда класс поиска в одном модуле M1 обращается к классу в другом модуле M2, помимо проверки битов режима доступа выполняются дополнительные проверки доступа. Объект Lookup с режимом PUBLIC и класс поиска в M1 может обращаться к открытым типам в M2, если M2 доступен для чтения из M1, а тип находится в пакете M2, экспортируемом как минимум для M1.

Объект Lookup для класса C также может переместиться в целевой класс с помощью методов Lookup.in и MethodHandles.privateLookupIn. При перемещении между модулями исходный класс поиска всегда записывается как предыдущий класс поиска, а доступ MODULE утрачивается. Если целевой класс находится в том же модуле, что и класс поиска C, целевой класс становится новым классом поиска, а предыдущий класс поиска не меняется. Если целевой класс находится в другом модуле относительно M1 (модуля C), C становится новым предыдущим классом поиска, а целевой класс — новым классом поиска. В этом случае, если в M0 уже был предыдущий класс поиска и он отличается от M1 и M2, результирующий поиск теряет все привилегии. Например:

Lookup lookup = MethodHandles.lookup();   // in class C
Lookup lookup2 = lookup.in(D.class);
MethodHandle mh = lookup2.findStatic(E.class, "m", MT);

Фабричный метод MethodHandles.lookup() возвращает объект Lookup без предыдущего класса поиска null. Метод lookup.in(D.class) преобразует поиск lookup для класса C в поиск для класса D без повышения привилегий. Если C и D находятся в одном модуле, lookup2 записывает D как новый класс поиска и сохраняет тот же предыдущий класс поиска, что и у исходного lookup, либо null, если предыдущего класса нет.

При перемещении объекта Lookup из класса одного гнезда в другое теряется доступ PRIVATE. При перемещении объекта Lookup из класса одного пакета в другой теряется доступ PACKAGE. При перемещении объекта Lookup из класса одного модуля в другой теряется доступ MODULE. Перемещение между модулями лишает возможности обращаться к неэкспортируемым классам как в модуле нового класса поиска, так и в модуле старого класса поиска, и результирующий объект Lookup сохраняет только доступ PUBLIC. Объект Lookup может перемещаться туда и обратно к классу в модуле класса поиска и в модуле предыдущего класса поиска. Перемещение между модулями может только уменьшить доступ, но не увеличить его. Перемещение в третий модуль приводит к утрате всех прав доступа.

В приведённом выше примере, если C и D находятся в разных модулях, lookup2 записывает D как класс поиска, а C — как предыдущий класс поиска, и lookup2 обладает только доступом PUBLIC. lookup2 может переместиться к другому классу в модуле C или в модуле D. Если класс E находится в третьем модуле, lookup2.in(E.class) создаёт объект Lookup для E без прав доступа, а класс поиска D объекта lookup2 записывается как предыдущий класс поиска.

Перемещение между модулями ограничивает доступ открытыми типами, к которым в равной степени могут обращаться как класс поиска, так и предыдущий класс поиска (см. ниже).

Метод MethodHandles.privateLookupIn(T.class, lookup) можно использовать для перемещения объекта lookup из класса C в класс T и создания нового объекта Lookup с закрытым доступом, если классу поиска разрешена глубокая рефлексия для T. Объект lookup должен обладать доступом MODULE и PRIVATE, чтобы вызвать privateLookupIn. Объект lookup для C в модуле M1 может выполнять глубокую рефлексию для всех классов в M1. Если T находится в M1, privateLookupIn создаёт новый объект Lookup для T с полными возможностями. Объект lookup для C также может выполнять глубокую рефлексию для T в другом модуле M2, если M1 читает M2, а M2 opens пакет, содержащий T, как минимум для M1. T становится новым классом поиска, а C — новым предыдущим классом поиска; объект Lookup, полученный в результате, теряет доступ MODULE. Полученный объект Lookup можно использовать для поиска членов или перемещения к другому классу поиска, вызвав Lookup::in. Однако его нельзя использовать для получения другого закрытого объекта Lookup с помощью вызова privateLookupIn, поскольку у него нет доступа MODULE.

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

Проверки доступа между модулями

Объект Lookup с режимом PUBLIC или UNCONDITIONAL допускает доступ между модулями. Проверка доступа выполняется относительно класса поиска и, если он есть, предыдущего класса поиска.

Объект Lookup с режимом UNCONDITIONAL может обращаться к открытым типам во всех модулях, если тип находится в пакете, экспортируемом безусловно.

Если объект Lookup для LC в M1 не имеет предыдущего класса поиска, поиск с режимом PUBLIC может обращаться ко всем открытым типам в модулях, доступных для чтения из M1, если тип находится в пакете, экспортируемом как минимум для M1.

Если объект Lookup для LC в M1 имеет предыдущий класс поиска PLC для M0, поиск с режимом PUBLIC может обращаться к пересечению всех открытых типов, доступных для M1, и всех открытых типов, доступных для M0. M0 читает M1, поэтому набор доступных типов включает:

  • пакеты, безусловно экспортируемые из M1
  • пакеты, безусловно экспортируемые из M0, если M1 читает M0
  • пакеты, безусловно экспортируемые из стороннего модуля M2, если и M0, и M1 читают M2
  • пакеты, экспортируемые по условию из M1 в M0
  • пакеты, экспортируемые по условию из M0 в M1, если M1 читает M0
  • пакеты, экспортируемые по условию из стороннего модуля M2 одновременно в M0 и M1, если и M0, и M1 читают M2

Режимы доступа

В таблице ниже представлены режимы доступа объекта Lookup, созданного одним из следующих фабричных методов или методов преобразования:
  • MethodHandles::lookup
  • MethodHandles::publicLookup
  • MethodHandles::privateLookupIn
  • Lookup::in
  • Lookup::dropLookupMode
Access mode summary
Объект поиска исходный защищённый закрытый пакет модуль открытый
CL = MethodHandles.lookup() в C ORI PRO PRI PAC MOD 1R
CL.in(C1) тот же пакет PAC MOD 1R
CL.in(C1) тот же модуль MOD 1R
CL.in(D) другой модуль 2R
CL.in(D).in(C) возврат к модулю 2R
PRI1 = privateLookupIn(C1,CL) PRO PRI PAC MOD 1R
PRI1a = privateLookupIn(C,PRI1) PRO PRI PAC MOD 1R
PRI1.in(C1) тот же пакет PAC MOD 1R
PRI1.in(C1) другой пакет MOD 1R
PRI1.in(D) другой модуль 2R
PRI1.dropLookupMode(PROTECTED) PRI PAC MOD 1R
PRI1.dropLookupMode(PRIVATE) PAC MOD 1R
PRI1.dropLookupMode(PACKAGE) MOD 1R
PRI1.dropLookupMode(MODULE) 1R
PRI1.dropLookupMode(PUBLIC) нет
PRI2 = privateLookupIn(D,CL) PRO PRI PAC 2R
privateLookupIn(D,PRI1) PRO PRI PAC 2R
privateLookupIn(C,PRI2) завершается ошибкой IAE
PRI2.in(D2) тот же пакет PAC 2R
PRI2.in(D2) другой пакет 2R
PRI2.in(C1) возврат к модулю 2R
PRI2.in(E) переход к третьему модулю нет
PRI2.dropLookupMode(PROTECTED) PRI PAC 2R
PRI2.dropLookupMode(PRIVATE) PAC 2R
PRI2.dropLookupMode(PACKAGE) 2R
PRI2.dropLookupMode(MODULE) 2R
PRI2.dropLookupMode(PUBLIC) нет
CL.dropLookupMode(PROTECTED) PRI PAC MOD 1R
CL.dropLookupMode(PRIVATE) PAC MOD 1R
CL.dropLookupMode(PACKAGE) MOD 1R
CL.dropLookupMode(MODULE) 1R
CL.dropLookupMode(PUBLIC) нет
PUB = publicLookup() U
PUB.in(D) другой модуль U
PUB.in(D).in(E) третий модуль U
PUB.dropLookupMode(UNCONDITIONAL) нет
privateLookupIn(C1,PUB) завершается ошибкой IAE
ANY.in(X), для недоступного X нет

Примечания:

  • Классы C и C1 находятся в модуле M1, а D и D2 — в модуле M2, а E — в модуле M3. X обозначает класс, недоступный для поиска. ANY обозначает любой из примеров поиска.
  • ORI означает, что установлен бит ORIGINAL, PRO означает, что установлен бит PROTECTED, PRI означает, что установлен бит PRIVATE, PAC означает, что установлен бит PACKAGE, MOD означает, что установлен бит MODULE, 1R и 2R означают, что установлен бит PUBLIC, U означает, что установлен бит UNCONDITIONAL, IAE означает, что выбрасывается IllegalAccessException.
  • Открытый доступ бывает трёх видов:
    • безусловный (U): поиск предполагает доступность для чтения. У объекта поиска отсутствует null предыдущий класс поиска.
    • чтение одним модулем (1R): проверка доступа к модулю выполняется относительно класса поиска. У объекта поиска отсутствует null предыдущий класс поиска.
    • чтение двумя модулями (2R): проверка доступа к модулю выполняется относительно класса поиска и предыдущего класса поиска. У объекта поиска имеется ненулевой предыдущий класс поиска, находящийся в модуле, отличном от модуля текущего класса поиска.
  • Любая попытка обратиться к третьему модулю приводит к потере всего доступа.
  • Если целевой класс X недоступен для Lookup::in, все режимы доступа сбрасываются.

Методы, чувствительные к вызывающему коду

Небольшое число методов Java обладают особым свойством, называемым чувствительностью к вызывающему коду. Метод, чувствительный к вызывающему коду, может вести себя по-разному в зависимости от идентификатора своего непосредственного вызывающего кода.

Если запрошен дескриптор метода для метода, чувствительного к вызывающему коду, применяются общие правила для поведения байт-кода, но они особым образом учитывают класс поиска. Полученный дескриптор метода ведёт себя так, как если бы он был вызван инструкцией, содержащейся в классе поиска, поэтому метод, чувствительный к вызывающему коду, распознаёт класс поиска. (В отличие от этого, вызывающий дескриптор метода игнорируется.) Поэтому в случае методов, чувствительных к вызывающему коду, разные классы поиска могут приводить к появлению дескрипторов методов с разным поведением.

Если объект поиска — это publicLookup() или какой-либо другой объект поиска без исходного доступа, класс поиска игнорируется. В таких случаях невозможно создать дескриптор метода, чувствительного к вызывающему коду, доступ запрещён, и поиск завершается с IllegalAccessException.

Обсуждение: Например, метод Class.forName(x), чувствительный к вызывающему коду, может возвращать разные классы или выбрасывать разные исключения в зависимости от загрузчика классов класса, который его вызывает. Общедоступный поиск Class.forName завершится неудачей, поскольку невозможно разумным образом определить поведение его байт-кода.

Если приложение кэширует дескрипторы методов для широкого совместного использования, ему следует создавать их с помощью publicLookup(). Поиск Class.forName завершится неудачей, и в этом случае приложение должно предпринять соответствующие действия. Возможно, последующий поиск, например во время вызова метода начальной загрузки, сможет учесть конкретный идентификатор вызывающего кода, сделав метод доступным.

Функция MethodHandles.lookup чувствительна к вызывающему коду, чтобы обеспечить безопасную основу для поисков. Почти все остальные методы API JSR 292 используют объекты поиска для проверки запросов доступа.

С момента появления:
1.7

Краткое описание вложенных классов

Модификатор и тип Класс Описание
static enum  MethodHandles.Lookup.ClassOption
Набор параметров класса, определяющих, добавляется ли скрытый класс, созданный методом Lookup::defineHiddenClass, динамически в качестве нового члена гнезда класса поиска и/или имеет ли скрытый класс прочную связь с загрузчиком классов, помеченным как его определяющий загрузчик.

Краткое описание полей

Модификатор и тип Поле Описание
static final int MODULE
Однобитовая маска, представляющая доступ module, который может повлиять на результат lookupModes.
static final int ORIGINAL
Однобитовая маска, представляющая доступ original, который может повлиять на результат lookupModes.
static final int PACKAGE
Однобитовая маска, представляющая доступ package (доступ по умолчанию), который может повлиять на результат lookupModes.
static final int PRIVATE
Однобитовая маска, представляющая доступ private, который может повлиять на результат lookupModes.
static final int PROTECTED
Однобитовая маска, представляющая доступ protected, который может повлиять на результат lookupModes.
static final int PUBLIC
Однобитовая маска, представляющая доступ public, который может повлиять на результат lookupModes.
static final int UNCONDITIONAL
Однобитовая маска, представляющая доступ unconditional, который может повлиять на результат lookupModes.

Краткое описание методов

Модификатор и тип Метод Описание
<T> Class<T> accessClass(Class<T> targetClass)
Определяет, доступен ли класс из контекста поиска, заданного этим объектом Lookup.
MethodHandle bind(Object receiver, String name, MethodType type)
Создаёт раннепривязанный дескриптор метода для нестатического метода.
Class<?> defineClass(byte[] bytes)
Создаёт и связывает класс или интерфейс из bytes с тем же загрузчиком классов, в том же пакете времени выполнения и домене защиты, что и класс поиска этого объекта поиска, как если бы был вызван ClassLoader::defineClass.
MethodHandles.Lookup defineHiddenClass(byte[] bytes, boolean initialize, MethodHandles.Lookup.ClassOption... options)
Создаёт скрытый класс или интерфейс из bytes и возвращает Lookup для только что созданного класса или интерфейса.
MethodHandles.Lookup defineHiddenClassWithClassData(byte[] bytes, Object classData, boolean initialize, MethodHandles.Lookup.ClassOption... options)
Создаёт скрытый класс или интерфейс из bytes с соответствующими данными класса и возвращает Lookup для только что созданного класса или интерфейса.
MethodHandles.Lookup dropLookupMode(int modeToDrop)
Создаёт объект поиска для того же класса поиска, чьи члены обнаруживает этот объект поиска, но с режимом поиска, в котором отсутствует указанный режим поиска.
<T> Class<T> ensureInitialized(Class<T> targetClass)
Гарантирует, что targetClass инициализирован.
Class<?> findClass(String targetName)
Ищет класс по имени в контексте поиска, заданном этим объектом Lookup, как если бы он разрешался инструкцией ldc.
MethodHandle findConstructor(Class<?> refc, MethodType type)
Создаёт дескриптор метода, который создаёт объект и инициализирует его с помощью конструктора указанного типа.
MethodHandle findGetter(Class<?> refc, String name, Class<?> type)
Создаёт дескриптор метода, предоставляющий доступ на чтение к нестатическому полю.
MethodHandle findSetter(Class<?> refc, String name, Class<?> type)
Создаёт дескриптор метода, предоставляющий доступ на запись к нестатическому полю.
MethodHandle findSpecial(Class<?> refc, String name, MethodType type, Class<?> specialCaller)
Создаёт раннепривязанный дескриптор метода для виртуального метода.
MethodHandle findStatic(Class<?> refc, String name, MethodType type)
Создаёт дескриптор метода для статического метода.
MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type)
Создаёт дескриптор метода, предоставляющий доступ на чтение к статическому полю.
MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type)
Создаёт дескриптор метода, предоставляющий доступ на запись к статическому полю.
VarHandle findStaticVarHandle(Class<?> decl, String name, Class<?> type)
Создаёт VarHandle, предоставляющий доступ к статическому полю name типа type, объявленному в классе типа decl.
VarHandle findVarHandle(Class<?> recv, String name, Class<?> type)
Создаёт VarHandle, предоставляющий доступ к нестатическому полю name типа type, объявленному в классе типа recv.
MethodHandle findVirtual(Class<?> refc, String name, MethodType type)
Создаёт дескриптор метода для виртуального метода.
boolean hasFullPrivilegeAccess()
Возвращает true, если этот объект поиска обладает полным привилегированным доступом, то есть
boolean hasPrivateAccess()
Устарел.
Этот метод изначально предназначался для проверки доступа PRIVATE, подразумевающего полный привилегированный доступ, однако с тех пор доступ MODULE стал независимым от доступа PRIVATE.
MethodHandles.Lookup in(Class<?> requestedLookupClass)
Создаёт объект поиска для указанного нового класса поиска.
Class<?> lookupClass()
Сообщает, какой класс выполняет поиск.
int lookupModes()
Сообщает, какие классы с защитой доступа к членам может создавать этот объект поиска.
Class<?> previousLookupClass()
Сообщает класс поиска в другом модуле, из которого этот объект поиска был ранее перенесён, или null.
MethodHandleInfo revealDirect(MethodHandle target)
Раскрывает прямой дескриптор метода, созданный этим или аналогичным объектом поиска.
String toString()
Отображает имя класса, из которого выполняется поиск, а затем символ «/» и имя предыдущего класса поиска, если он есть.
MethodHandle unreflect(Method m)
Создаёт прямой дескриптор метода для m, если класс поиска обладает необходимыми правами.
MethodHandle unreflectConstructor(Constructor<?> c)
Создаёт дескриптор метода для отражённого конструктора.
MethodHandle unreflectGetter(Field f)
Создаёт дескриптор метода, предоставляющий доступ на чтение к отражённому полю.
MethodHandle unreflectSetter(Field f)
Создаёт дескриптор метода, предоставляющий доступ на запись к отражённому полю.
MethodHandle unreflectSpecial(Method m, Class<?> specialCaller)
Создаёт дескриптор метода для отражённого метода.
VarHandle unreflectVarHandle(Field f)
Создаёт VarHandle, предоставляющий доступ к отражённому полю f типа T, объявленному в классе типа R.

Методы, объявленные в классе Object

clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait

Подробное описание полей

PUBLIC

public static final int PUBLIC
Однобитная маска, представляющая доступ public, который может влиять на результат lookupModes. Значение 0x01 случайно совпадает со значением public бита-модификатора.

Lookup с этим режимом поиска выполняет проверку доступа между модулями относительно класса поиска и предыдущего класса поиска, если он есть.

См. также:
  • Значения константных полей

PRIVATE

public static final int PRIVATE
Однобитная маска, представляющая доступ private, который может влиять на результат lookupModes. Значение 0x02 случайно совпадает со значением private бита-модификатора.
См. также:
  • Значения константных полей

PROTECTED

public static final int PROTECTED
Однобитная маска, представляющая доступ protected, который может влиять на результат lookupModes. Значение 0x04 случайно совпадает со значением protected бита-модификатора.
См. также:
  • Значения константных полей

PACKAGE

public static final int PACKAGE
Однобитная маска, представляющая доступ package (доступ по умолчанию), который может влиять на результат lookupModes. Значение — 0x08, которое не имеет значимого соответствия какому-либо конкретному биту-модификатору.
См. также:
  • Значения константных полей

MODULE

public static final int MODULE
Однобитная маска, представляющая доступ module, который может влиять на результат lookupModes. Значение — 0x10, которое не имеет значимого соответствия какому-либо конкретному биту-модификатору. В сочетании с битом-модификатором PUBLIC объект Lookup с этим режимом поиска может обращаться ко всем открытым типам в модуле класса поиска и к открытым типам в пакетах, экспортируемых другими модулями в модуль класса поиска.

Если этот режим поиска установлен, предыдущий класс поиска всегда равен null.

Начиная с:
9
См. также:
  • Значения константных полей

UNCONDITIONAL

public static final int UNCONDITIONAL
Однобитная маска, представляющая доступ unconditional, который может влиять на результат lookupModes. Значение — 0x20, которое не имеет значимого соответствия какому-либо конкретному биту-модификатору. Объект Lookup с этим режимом поиска предполагает наличие доступности для чтения. Этот режим поиска позволяет обращаться ко всем открытым членам открытых типов всех модулей, если тип находится в пакете, который exported unconditionally.

Если этот режим поиска установлен, предыдущий класс поиска всегда равен null.

Начиная с:
9
См. также:
  • MethodHandles.publicLookup()
  • Значения константных полей

ORIGINAL

public static final int ORIGINAL
Однобитная маска, представляющая доступ original, который может влиять на результат lookupModes. Значение — 0x40, которое не имеет значимого соответствия какому-либо конкретному биту-модификатору.

Если этот режим поиска установлен, объект Lookup должен быть создан исходным классом поиска вызовом метода MethodHandles.lookup() или методом начальной загрузки, вызванным виртуальной машиной. Объект Lookup с этим режимом поиска обладает полным привилегированным доступом.

Начиная с:
16
См. также:
  • Значения константных полей

Подробное описание методов

lookupClass

public Class<?> lookupClass()
Сообщает, какой класс выполняет поиск. Именно относительно этого класса выполняются проверки видимости и разрешений на доступ.

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

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

Возвращает:
класс поиска, от имени которого этот объект поиска находит члены
См. также:
  • Поиск между модулями

previousLookupClass

public Class<?> previousLookupClass()
Возвращает класс поиска из другого модуля, из которого этот объект поиска был ранее перенесён, либо null.

У объекта Lookup, созданного фабричными методами, такими как lookup() и publicLookup(), предыдущий класс поиска null. У объекта Lookup предыдущий класс поиска не равен null, если этот поиск был перенесён из прежнего класса поиска в одном модуле в новый класс поиска в другом модуле.

Возвращает:
класс поиска из другого модуля, из которого этот объект поиска был ранее перенесён, либо null
Начиная с:
14
См. также:
  • in(Class)
  • MethodHandles.privateLookupIn(Class, Lookup)
  • Поиск между модулями

lookupModes

public int lookupModes()
Сообщает, к каким классам защиты доступа членов может предоставлять доступ этот объект поиска. Результат — битовая маска из битов PUBLIC (0x01), PRIVATE (0x02), PROTECTED (0x04), PACKAGE (0x08), MODULE (0x10), UNCONDITIONAL (0x20) и ORIGINAL (0x40).

Недавно созданный объект поиска для класса вызывающего кода имеет все возможные биты, кроме UNCONDITIONAL. У объекта поиска для нового класса поиска, созданного на основе предыдущего объекта поиска, некоторые биты режима могут быть сброшены. Биты режима также можно сбросить напрямую. После сброса биты режима нельзя восстановить из объекта поиска с ограниченными правами. Это позволяет ограничить доступ через новый объект поиска так, чтобы он мог получать доступ только к именам, доступным исходному объекту поиска, а также новому классу поиска.

Возвращает:
режимы поиска, ограничивающие типы доступа, выполняемого этим объектом поиска
См. также:
  • in(Class)
  • dropLookupMode(int)

in

public MethodHandles.Lookup in(Class<?> requestedLookupClass)
Создаёт объект поиска для указанного нового класса поиска. Полученный объект будет указывать указанный класс как собственный lookupClass.

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

  • Если новый класс поиска отличается от старого класса поиска, то есть утрачивается доступ ORIGINAL.
  • Если новый класс поиска находится в модуле, отличном от модуля старого класса, то есть утрачивается доступ MODULE.
  • Если новый класс поиска находится в пакете, отличном от пакета старого класса, защищённые и члены с доступом по умолчанию (пакетным доступом) будут недоступны, то есть утрачивается доступ PROTECTED и PACKAGE.
  • Если новый класс поиска не входит в тот же пакет, что и старый класс, приватные члены будут недоступны, а защищённые члены будут недоступны по наследованию, то есть утрачивается доступ PRIVATE. (Защищённые члены могут оставаться доступными благодаря принадлежности к одному пакету.)
  • Если новый класс поиска недоступен этому объекту поиска, то никакие члены, даже публичные, не будут доступны; то есть утрачиваются все режимы доступа.
  • Если новый класс поиска, старый класс поиска и предыдущий класс поиска находятся в разных модулях, то есть выполняется перенос в третий модуль, утрачиваются все режимы доступа.

Новый предыдущий класс поиска выбирается следующим образом:

  • Если у нового объекта поиска установлен бит UNCONDITIONAL, новый предыдущий класс поиска равен null.
  • Если новый класс поиска находится в том же модуле, что и старый класс поиска, новым предыдущим классом поиска становится старый предыдущий класс поиска.
  • Если новый класс поиска находится в модуле, отличном от модуля старого класса поиска, новым предыдущим классом поиска становится старый класс поиска.

Возможности полученного объекта поиска по загрузке классов (используемые при вызовах findClass(String)) определяются загрузчиком класса поиска, который может измениться в результате этой операции.

Параметры:
requestedLookupClass — желаемый класс поиска для нового объекта поиска
Возвращает:
объект поиска, указывающий желаемый класс поиска, либо тот же объект, если изменений нет
Выбрасывает:
IllegalArgumentException — если requestedLookupClass является примитивным типом, void или массивом
NullPointerException — если аргумент равен null
См. также:
  • accessClass(Class)
  • Поиск между модулями

dropLookupMode

public MethodHandles.Lookup dropLookupMode(int modeToDrop)
Создаёт объект поиска для того же класса поиска, чьи члены находит этот объект, но с режимом поиска, из которого удалён указанный режим. Удаляемый режим поиска должен быть одним из следующих: PUBLIC, MODULE, PACKAGE, PROTECTED, PRIVATE, ORIGINAL или UNCONDITIONAL.

Если этот объект поиска является публичным объектом поиска, для него установлен режим UNCONDITIONAL и не установлен ни один другой режим. При удалении UNCONDITIONAL из публичного объекта поиска полученный объект поиска не будет иметь доступа.

Если этот объект поиска не является публичным объектом поиска, следующие правила применяются независимо от его режимов поиска. Режимы PROTECTED и ORIGINAL всегда удаляются, поэтому полученный режим поиска никогда не будет предоставлять эти возможности доступа. При удалении PACKAGE полученный объект поиска не будет иметь доступа PACKAGE или PRIVATE. При удалении MODULE полученный объект поиска не будет иметь доступа MODULE, PACKAGE или PRIVATE. При удалении PUBLIC полученный объект поиска не будет иметь доступа.

Примечание API:
Объект поиска с режимом PACKAGE, но без режима PRIVATE может безопасно делегировать непубличный доступ в пакете класса поиска, не предоставляя приватный доступ. Объект поиска с режимом MODULE, но без режима PACKAGE может безопасно делегировать доступ PUBLIC в модуле класса поиска, не предоставляя доступ к пакету. Объект поиска с предыдущим классом поиска (и режимом PUBLIC, но без режима MODULE) может безопасно делегировать доступ к публичным классам, доступным как модулю класса поиска, так и модулю предыдущего класса поиска.
Параметры:
modeToDrop — удаляемый режим поиска
Возвращает:
объект поиска, в котором отсутствует указанный режим, либо тот же объект, если изменений нет
Выбрасывает:
IllegalArgumentException — если modeToDrop не является одним из PUBLIC, MODULE, PACKAGE, PROTECTED, PRIVATE, ORIGINAL или UNCONDITIONAL
Начиная с:
9
См. также:
  • MethodHandles.privateLookupIn(Class, MethodHandles.Lookup)

defineClass

public Class<?> defineClass(byte[] bytes) throws IllegalAccessException
Создаёт и связывает класс или интерфейс из bytes с тем же загрузчиком классов, в том же пакете времени выполнения и с тем же доменом защиты, что и класс поиска этого объекта, как если бы был вызван ClassLoader::defineClass.

Режимы поиска этого объекта должны включать доступ PACKAGE, поскольку класс сможет обращаться к членам с доступом по умолчанию (пакетным доступом). Режим поиска PACKAGE служит для подтверждения того, что объект поиска создан вызывающим кодом из пакета времени выполнения (или получен из объекта поиска, изначально созданного кодом с достаточными привилегиями для целевого класса в этом пакете).

Параметр bytes содержит байты корректного файла класса (определённого в Спецификации виртуальной машины Java) с именем класса из того же пакета, что и класс поиска.

Этот метод не запускает инициализатор класса. Инициализатор класса может быть запущен позднее, как описано в разделе 12.4 Спецификации языка Java.

Параметры:
bytes — байты класса
Возвращает:
объект Class для класса
Выбрасывает:
IllegalAccessException — если этот объект поиска не имеет доступа PACKAGE
ClassFormatError — если bytes не является структурой ClassFile
IllegalArgumentException — если bytes обозначает класс из пакета, отличного от пакета класса поиска, или если bytes не является классом или интерфейсом (в значении элемента access_flags установлен флаг ACC_MODULE)
VerifyError — если вновь созданный класс не прошёл проверку
LinkageError — если вновь созданный класс не удалось связать по какой-либо другой причине
NullPointerException — если bytes равен null
Начиная с:
9
См. также:
  • MethodHandles.privateLookupIn(Class, MethodHandles.Lookup)
  • dropLookupMode(int)
  • ClassLoader.defineClass(String,byte[],int,int,ProtectionDomain)

defineHiddenClass

public MethodHandles.Lookup defineHiddenClass(byte[] bytes, boolean initialize, MethodHandles.Lookup.ClassOption... options) throws IllegalAccessException
Создаёт скрытый класс или интерфейс из bytes и возвращает Lookup для только что созданного класса или интерфейса.

Обычно класс или интерфейс C создаётся загрузчиком классов, который либо определяет C напрямую, либо делегирует это другому загрузчику классов. Загрузчик классов определяет C напрямую, вызывая ClassLoader::defineClass, в результате чего виртуальная машина Java получает C из предполагаемого представления в формате файла class. В ситуациях, когда использование загрузчика классов нежелательно, вместо этого класс или интерфейс C можно создать с помощью данного метода. Этот метод позволяет определить C и тем самым создать его, не вызывая ClassLoader::defineClass. Вместо этого данный метод определяет C, как если бы виртуальной машине Java было поручено получить класс или интерфейс, не являющийся массивом, C из предполагаемого представления в формате файла class согласно следующим правилам:

  1. Режимы поиска для данного Lookup должны включать доступ с полными привилегиями. Такой уровень доступа необходим для создания C в модуле класса поиска данного Lookup.
  2. Предполагаемое представление в формате bytes должно быть структурой ClassFile (JVMS 4.1) поддерживаемой основной и дополнительной версии. Основная и дополнительная версии могут отличаться от версии файла class класса поиска данного Lookup.
  3. Значение this_class должно быть допустимым индексом в таблице constant_pool, а запись по этому индексу должна быть допустимой структурой CONSTANT_Class_info. Пусть N — это двоичное имя во внутренней форме, заданное этой структурой. N должно обозначать класс или интерфейс в том же пакете, что и класс поиска.
  4. Пусть CN — это строка N + "." + <suffix>, где <suffix> — неквалифицированное имя.

    Пусть newBytes — это структура ClassFile, заданная bytes, с дополнительной записью в таблице constant_pool, указывающей на структуру CONSTANT_Utf8_info для CN, причём структура CONSTANT_Class_info, указанная this_class, ссылается на новую структуру CONSTANT_Utf8_info.

    Пусть L — это определяющий загрузчик классов класса поиска данного Lookup.

    C создаётся с именем CN, загрузчиком классов L и предполагаемым представлением newBytes, как если бы это выполнялось по правилам JVMS 5.3.5, со следующими изменениями:

    • Константе, указанной this_class, разрешено задавать имя, содержащее один символ ".", хотя это не является допустимым двоичным именем класса или интерфейса во внутренней форме.
    • Виртуальная машина Java помечает L как определяющий загрузчик классов для C, но ни один загрузчик классов не записывается как инициирующий загрузчик классов для C.
    • Считается, что C имеет тот же динамический пакет, модуль и домен защиты, что и класс поиска данного Lookup.
    • Пусть GN — это двоичное имя, полученное заменой косых черт ASCII на точки ASCII в N (двоичном имени, записанном во внутренней форме). Для экземпляра Class, представляющего C:
      • Class.getName() возвращает строку GN + "/" + <suffix>, хотя это не является допустимым двоичным именем класса или интерфейса.
      • Class.descriptorString() возвращает строку "L" + N + "." + <suffix> + ";", хотя это не является допустимым именем дескриптора типа.
      • Class.describeConstable() возвращает пустой объект optional, поскольку C нельзя описать в номинальной форме.

После создания C виртуальная машина Java связывает его. Связывание выполняется согласно JVMS 5.4.3 со следующими изменениями:

  • Во время проверки, если необходимо загрузить класс с именем CN, попытка завершается успешно и возвращает класс C. Запрос к какому-либо загрузчику классов не выполняется.
  • При любой попытке разрешить запись в пуле констант времени выполнения, указанную this_class, считается, что символьная ссылка разрешена в C, и разрешение всегда немедленно завершается успешно.

Если параметр initialize имеет значение true, виртуальная машина Java инициализирует C.

Только что созданный класс или интерфейс C служит классом поиска объекта Lookup, возвращаемого этим методом. C является скрытым в том смысле, что ни один другой класс или интерфейс не может ссылаться на C через запись в пуле констант. То есть скрытый класс или интерфейс не может быть указан другим классом в качестве суперкласса, типа поля, типа параметра метода или типа возвращаемого значения метода. Это объясняется тем, что у скрытого класса или интерфейса нет двоичного имени, поэтому отсутствует внутренняя форма, которую можно было бы записать в пул констант какого-либо класса. Скрытый класс или интерфейс нельзя обнаружить с помощью Class.forName(String, boolean, ClassLoader), ClassLoader.loadClass(String, boolean) или findClass(String), а также нельзя изменить с помощью Java-агентов или инструментальных агентов, использующих интерфейс инструментирования виртуальной машины Java (JVM Tool Interface).

Класс или интерфейс, созданный загрузчиком классов, имеет с ним прочную связь. То есть каждый объект Class содержит ссылку на ClassLoader, который определил его. Это означает, что класс, созданный загрузчиком классов, может быть выгружен только в том случае, если его определяющий загрузчик недостижим и, следовательно, может быть освобождён сборщиком мусора (JLS 12.7). Однако по умолчанию скрытый класс или интерфейс может быть выгружен, даже если загрузчик классов, помеченный как его определяющий загрузчик, достижим. Такое поведение полезно, когда скрытый класс или интерфейс используется несколькими классами, определёнными произвольными загрузчиками классов. В других случаях скрытый класс или интерфейс может быть связан с одним классом (или небольшим числом классов) с тем же определяющим загрузчиком, что и у скрытого класса или интерфейса. В таких случаях, когда время жизни скрытого класса или интерфейса должно совпадать со временем жизни обычного класса или интерфейса, в options можно передать параметр STRONG. Это обеспечивает скрытому классу такую же прочную связь с загрузчиком классов, помеченным как его определяющий загрузчик, какую обычный класс или интерфейс имеет со своим определяющим загрузчиком. Если STRONG не используется, вызывающий defineHiddenClass всё ещё может предотвратить выгрузку скрытого класса или интерфейса, обеспечив достижимость объекта Class.

Характеристики выгрузки устанавливаются для каждого скрытого класса при его создании и не могут быть изменены позднее. Возможность выгружать скрытые классы независимо от загрузчика классов, помеченного как их определяющий загрузчик, позволяет приложению создавать очень большое количество скрытых классов. Напротив, если используется STRONG, JVM может исчерпать память, как и в случае создания обычных классов загрузчиками классов.

Классам и интерфейсам в одном гнезде разрешён взаимный доступ к закрытым членам. Отношение гнезда определяется атрибутом NestHost (JVMS 4.7.28) и атрибутом NestMembers (JVMS 4.7.29) в файле class. По умолчанию скрытый класс входит в гнездо, состоящее только из него самого, поскольку у скрытого класса нет двоичного имени. Параметр NESTMATE можно передать в options, чтобы создать скрытый класс или интерфейс C в качестве участника гнезда. Гнездо, к которому относится C, не определяется каким-либо атрибутом NestHost в структуре ClassFile, из которой был создан C. Вместо этого хозяин гнезда для C определяется следующими правилами:

  • Если хозяин гнезда класса поиска данного Lookup уже был определён, пусть H будет хозяином гнезда класса поиска. В противном случае хозяин гнезда класса поиска определяется с помощью алгоритма из JVMS 5.4.4, результатом которого является H.
  • Хозяином гнезда для C определяется H — хозяин гнезда класса поиска.

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

Параметры:
bytes — байты, составляющие данные класса, в формате допустимого файла class, определённом в Спецификации виртуальной машины Java.
initialize — если true, класс будет инициализирован.
options — параметры класса
Возвращает:
объект Lookup для скрытого класса с доступом original и с полными привилегиями
Вызывает исключение:
IllegalAccessException — если данный Lookup не имеет доступа с полными привилегиями
ClassFormatError — если bytes не является структурой ClassFile
UnsupportedClassVersionError — если bytes имеет неподдерживаемую основную или дополнительную версию
IllegalArgumentException — если bytes обозначает класс в пакете, отличном от пакета класса поиска, или если bytes не является классом или интерфейсом (в значении элемента access_flags установлен флаг ACC_MODULE)
IncompatibleClassChangeError — если класс или интерфейс, указанный в качестве непосредственного суперкласса для C, на самом деле является интерфейсом либо если любой из классов или интерфейсов, указанных в качестве непосредственных суперинтерфейсов для C, на самом деле не является интерфейсом
ClassCircularityError — если любой из суперклассов или суперинтерфейсов C является самим C
VerifyError — если только что созданный класс не удалось проверить
LinkageError — если только что созданный класс не удалось связать по какой-либо другой причине
NullPointerException — если любой параметр равен null
См. Спецификацию языка Java:
12.7 Выгрузка классов и интерфейсов
См. Спецификацию виртуальной машины Java:
4.2.1 Двоичные имена классов и интерфейсов
4.2.2 Неквалифицированные имена
4.7.28 Атрибут NestHost
4.7.29 Атрибут NestMembers
5.4.3.1 Разрешение классов и интерфейсов
5.4.4 Контроль доступа
5.3.5 Создание Class из представления файла class
5.4 Связывание
5.5 Инициализация
Начиная с версии:
15
См. также:
  • Class.isHidden()

defineHiddenClassWithClassData

public MethodHandles.Lookup defineHiddenClassWithClassData(byte[] bytes, Object classData, boolean initialize, MethodHandles.Lookup.ClassOption... options) throws IllegalAccessException
Создаёт скрытый класс или интерфейс из bytes с соответствующими данными класса и возвращает Lookup для только что созданного класса или интерфейса.

Этот метод эквивалентен вызову defineHiddenClass(bytes, initialize, options), как если бы в скрытый класс было внедрено закрытое статическое final-поле без имени, инициализируемое заданным classData первой инструкцией инициализатора класса. Только что созданный класс связывается виртуальной машиной Java.

Методы MethodHandles::classData и MethodHandles::classDataAt можно использовать для получения classData.

Примечание к API:
Фреймворк может создать скрытый класс с данными класса, содержащими один или несколько объектов, а затем загрузить данные класса в виде динамически вычисляемых констант с помощью метода начальной загрузки. Метод Class data доступен только объекту поиска, созданному только что определённым скрытым классом, но недоступен другим участникам того же гнезда (в отличие от закрытых статических полей, доступных участникам гнезда). Следует учитывать изменяемость данных, например при передаче через данные класса массива или другой изменяемой структуры. Изменение любого значения, хранящегося в данных класса во время выполнения, может привести к непредсказуемому поведению. Если данные класса представляют собой List, рекомендуется сделать их неизменяемыми, например с помощью List::of.
Параметры:
bytes — байты класса
classData — предварительно инициализированные данные класса
initialize — если true, класс будет инициализирован.
options — параметры класса
Возвращает:
объект Lookup для скрытого класса с доступом original и с полными привилегиями
Вызывает исключение:
IllegalAccessException — если данный Lookup не имеет доступа с полными привилегиями
ClassFormatError — если bytes не является структурой ClassFile
UnsupportedClassVersionError — если bytes имеет неподдерживаемую основную или дополнительную версию
IllegalArgumentException — если bytes обозначает класс в пакете, отличном от пакета класса поиска, или если bytes не является классом или интерфейсом (в значении элемента access_flags установлен флаг ACC_MODULE)
IncompatibleClassChangeError — если класс или интерфейс, указанный в качестве непосредственного суперкласса для C, на самом деле является интерфейсом либо если любой из классов или интерфейсов, указанных в качестве непосредственных суперинтерфейсов для C, на самом деле не является интерфейсом
ClassCircularityError — если любой из суперклассов или суперинтерфейсов C является самим C
VerifyError — если только что созданный класс не удалось проверить
LinkageError — если только что созданный класс не удалось связать по какой-либо другой причине
NullPointerException — если любой параметр равен null
См. Спецификацию языка Java:
12.7 Выгрузка классов и интерфейсов
См. Спецификацию виртуальной машины Java:
4.2.1 Двоичные имена классов и интерфейсов
4.2.2 Неквалифицированные имена
4.7.28 Атрибут NestHost
4.7.29 Атрибут NestMembers
5.4.3.1 Разрешение классов и интерфейсов
5.4.4 Контроль доступа
5.3.5 Создание Class из представления файла class
5.4 Связывание
5.5 Инициализация
Начиная с версии:
16
См. также:
  • defineHiddenClass(byte[], boolean, ClassOption...)
  • Class.isHidden()
  • MethodHandles.classData(Lookup, String, Class)
  • MethodHandles.classDataAt(Lookup, String, Class, int)

toString

public String toString()
Отображает имя класса, из которого выполняется поиск, за которым следуют символ «/» и имя предыдущего класса поиска, если он есть. (Используется имя, возвращаемое методом Class.getName.) Если доступ, разрешённый этому объекту поиска, ограничен, к имени класса добавляется суффикс, состоящий из косой черты и ключевого слова. Ключевое слово обозначает наиболее высокий разрешённый уровень доступа и выбирается следующим образом:
  • Если доступ не разрешён, суффикс — «/noaccess».
  • Если разрешён только безусловный доступ, суффикс — «/publicLookup».
  • Если разрешён только открытый доступ к типам в экспортируемых пакетах, суффикс — «/public».
  • Если разрешён только открытый доступ и доступ к модулю, суффикс — «/module».
  • Если разрешён открытый доступ и доступ к пакету, суффикс — «/package».
  • Если разрешены открытый, пакетный и закрытый доступ, суффикс — «/private».
Если не подходит ни один из перечисленных случаев, значит, разрешён доступ с полными привилегиями (открытый, к модулю, к пакету, закрытый и защищённый). В этом случае суффикс не добавляется. Это верно только для объекта, изначально полученного с помощью MethodHandles.lookup. Объекты, созданные с помощью Lookup.in, всегда имеют ограниченный доступ, поэтому в их представлении отображается суффикс.

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

Переопределяет:
toString в классе Object
Возвращает:
строковое представление объекта
См. также:
  • in(Class)

findStatic

public MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException
Создаёт дескриптор метода для статического метода. Тип дескриптора метода будет совпадать с типом метода. (Поскольку статические методы не принимают получателей, в тип дескриптора метода не добавляется дополнительный аргумент-получатель, как это происходит с findVirtual или findSpecial.) Метод и все его типы аргументов должны быть доступны объекту поиска.

Возвращаемый дескриптор метода будет иметь переменную арность тогда и только тогда, когда установлен бит модификатора переменной арности метода (0x0080).

При вызове возвращаемого дескриптора метода класс метода будет инициализирован, если он ещё не был инициализирован.

Пример:

import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
...
MethodHandle MH_asList = publicLookup().findStatic(Arrays.class,
  "asList", methodType(List.class, Object[].class));
assertEquals("[x, y]", MH_asList.invoke("x", "y").toString());
Параметры:
refc — класс, из которого осуществляется доступ к методу
name — имя метода
type — тип метода
Возвращает:
требуемый дескриптор метода
Вызывает исключение:
NoSuchMethodException — если метод не существует
IllegalAccessException — если проверка доступа завершается неудачей, метод не является static либо установлен бит модификатора переменной арности метода и проверка asVarargsCollector завершается неудачей
NullPointerException — если какой-либо аргумент равен null

findVirtual

public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException
Создаёт дескриптор метода для виртуального метода. Тип дескриптора метода будет совпадать с типом метода, к которому добавлен тип получателя (обычно refc). Метод и все его типы аргументов должны быть доступны объекту поиска.

При вызове дескриптор рассматривает первый аргумент как получателя и для не закрытых методов выбирает реализацию метода на основе типа получателя. Для закрытых методов именованный метод в refc будет вызван для получателя. (Действие диспетчеризации идентично действию, выполняемому инструкцией invokevirtual или invokeinterface.)

Первый аргумент будет иметь тип refc, если класс поиска обладает полными привилегиями для доступа к члену. В противном случае член должен быть protected, а тип первого аргумента будет ограничен классом поиска.

Возвращаемый дескриптор метода будет иметь переменную арность тогда и только тогда, когда установлен бит модификатора переменной арности метода (0x0080).

В силу общей эквивалентности инструкций invokevirtual и дескрипторов методов, создаваемых с помощью findVirtual, если класс является MethodHandle, а строка имени равна invokeExact или invoke, результирующий дескриптор метода эквивалентен дескриптору, созданному с помощью MethodHandles.exactInvoker или MethodHandles.invoker с тем же аргументом type.

Если класс является VarHandle, а строка имени соответствует имени метода режима доступа с полиморфной сигнатурой, результирующий дескриптор метода эквивалентен дескриптору, созданному с помощью MethodHandles.varHandleInvoker(VarHandle.AccessMode, MethodType) с режимом доступа, соответствующим строке имени, и теми же аргументами type.

Пример:

import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
...
MethodHandle MH_concat = publicLookup().findVirtual(String.class,
  "concat", methodType(String.class, String.class));
MethodHandle MH_hashCode = publicLookup().findVirtual(Object.class,
  "hashCode", methodType(int.class));
MethodHandle MH_hashCode_String = publicLookup().findVirtual(String.class,
  "hashCode", methodType(int.class));
assertEquals("xy", (String) MH_concat.invokeExact("x", "y"));
assertEquals("xy".hashCode(), (int) MH_hashCode.invokeExact((Object)"xy"));
assertEquals("xy".hashCode(), (int) MH_hashCode_String.invokeExact("xy"));
// interface method:
MethodHandle MH_subSequence = publicLookup().findVirtual(CharSequence.class,
  "subSequence", methodType(CharSequence.class, int.class, int.class));
assertEquals("def", MH_subSequence.invoke("abcdefghi", 3, 6).toString());
// constructor "internal method" must be accessed differently:
MethodType MT_newString = methodType(void.class); //()V for new String()
try { assertEquals("impossible", lookup()
        .findVirtual(String.class, "<init>", MT_newString));
 } catch (NoSuchMethodException ex) { } // OK
MethodHandle MH_newString = publicLookup()
  .findConstructor(String.class, MT_newString);
assertEquals("", (String) MH_newString.invokeExact());
Параметры:
refc — класс или интерфейс, из которого осуществляется доступ к методу
name — имя метода
type — тип метода без аргумента-получателя
Возвращает:
требуемый дескриптор метода
Вызывает исключение:
NoSuchMethodException — если метод не существует
IllegalAccessException — если проверка доступа завершается неудачей, метод является static либо установлен бит модификатора переменной арности метода и проверка asVarargsCollector завершается неудачей
NullPointerException — если какой-либо аргумент равен null

findConstructor

public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException
Создаёт дескриптор метода, который создаёт объект и инициализирует его с помощью конструктора указанного типа. Типы параметров дескриптора метода совпадают с типами параметров конструктора, а тип возвращаемого значения — это ссылка на класс конструктора. Конструктор и все его типы аргументов должны быть доступны объекту поиска.

Указанный тип должен иметь тип возвращаемого значения void. (Это соответствует тому, как JVM обрабатывает дескрипторы типов конструкторов.)

Возвращаемый дескриптор метода будет иметь переменную арность тогда и только тогда, когда установлен бит модификатора переменной арности конструктора (0x0080).

При вызове возвращаемого дескриптора метода класс конструктора будет инициализирован, если он ещё не был инициализирован.

Пример:

import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
...
MethodHandle MH_newArrayList = publicLookup().findConstructor(
  ArrayList.class, methodType(void.class, Collection.class));
Collection orig = Arrays.asList("x", "y");
Collection copy = (ArrayList) MH_newArrayList.invokeExact(orig);
assert(orig != copy);
assertEquals(orig, copy);
// a variable-arity constructor:
MethodHandle MH_newProcessBuilder = publicLookup().findConstructor(
  ProcessBuilder.class, methodType(void.class, String[].class));
ProcessBuilder pb = (ProcessBuilder)
  MH_newProcessBuilder.invoke("x", "y", "z");
assertEquals("[x, y, z]", pb.command().toString());
Параметры:
refc — класс или интерфейс, из которого осуществляется доступ к методу
type — тип метода без аргумента-получателя и с типом возвращаемого значения void
Возвращает:
требуемый дескриптор метода
Вызывает исключение:
NoSuchMethodException — если конструктор не существует
IllegalAccessException — если проверка доступа завершается неудачей либо установлен бит модификатора переменной арности метода и проверка asVarargsCollector завершается неудачей
NullPointerException — если какой-либо аргумент равен null

findClass

public Class<?> findClass(String targetName) throws ClassNotFoundException, IllegalAccessException
Ищет класс по имени в контексте поиска, определяемом этим объектом Lookup, как если бы он был разрешён инструкцией ldc. Такое разрешение, описанное в JVMS 5.4.3.1, пытается найти и загрузить класс, а затем определяет, доступен ли класс этому объекту поиска.

Для класса или интерфейса именем является двоичное имя. Для массива размерности n имя начинается с n вхождений '[', за которыми следует тип элемента, закодированный в таблице, указанной в Class.getName().

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

Параметры:
targetName — двоичное имя класса или строка, представляющая класс массива
Возвращает:
запрошенный класс.
Исключения:
LinkageError — если связывание завершается с ошибкой
ClassNotFoundException — если класс не может быть загружен загрузчиком класса поиска.
IllegalAccessException — если класс недоступен в разрешённых режимах доступа.
NullPointerException — если targetName равно null
См. Спецификацию виртуальной машины Java:
5.4.3.1 Разрешение классов и интерфейсов
Начиная с версии:
9

ensureInitialized

public <T> Class<T> ensureInitialized(Class<T> targetClass) throws IllegalAccessException
Обеспечивает инициализацию targetClass. Инициализируемый класс должен быть доступен этому объекту Lookup. Этот метод инициирует инициализацию targetClass, если она ещё не была выполнена, как указано в JVMS 5.5.

Метод возвращает управление, когда targetClass полностью инициализирован или когда targetClass инициализируется текущим потоком.

Параметры типа:
T — тип инициализируемого класса
Параметры:
targetClass — инициализируемый класс
Возвращает:
targetClass, который был инициализирован или инициализируется текущим потоком.
Исключения:
IllegalArgumentException — если targetClass имеет примитивный тип, является void или массивом
IllegalAccessException — если targetClass недоступен этому объекту поиска
ExceptionInInitializerError — если инициализация класса, вызванная этим методом, завершается с ошибкой
См. Спецификацию виртуальной машины Java:
5.5 Инициализация
Начиная с версии:
15

accessClass

public <T> Class<T> accessClass(Class<T> targetClass) throws IllegalAccessException
Определяет, доступен ли класс из контекста поиска, определяемого этим объектом Lookup. Статический инициализатор класса не запускается. Если targetClass является классом массива, targetClass доступен, если доступен тип элемента класса массива. В противном случае доступность targetClass определяется следующим образом.

Если targetClass находится в том же модуле, что и класс поиска, класс поиска находится LC в модуле M1, а предыдущий класс поиска находится в модуле M0 или null, если он отсутствует, то targetClass доступен тогда и только тогда, когда выполняется одно из следующих условий:

  • Если у этого поиска есть доступ PRIVATE, targetClass — это LC или другой класс в том же гнезде, что и LC.
  • Если у этого поиска есть доступ PACKAGE, targetClass находится в том же пакете времени выполнения, что и LC.
  • Если у этого поиска есть доступ MODULE, targetClass является открытым типом в M1.
  • Если у этого поиска есть доступ PUBLIC, targetClass является открытым типом в пакете, экспортируемом M1 как минимум для M0, если предыдущий класс поиска присутствует; в противном случае targetClass является открытым типом в пакете, безусловно экспортируемом M1.

В противном случае, если у этого поиска есть доступ UNCONDITIONAL, он может получать доступ к открытым типам во всех модулях, если тип находится в безусловно экспортируемом пакете.

В противном случае targetClass находится в модуле, отличном от lookupClass, и если у этого поиска нет доступа PUBLIC, то lookupClass недоступен.

В противном случае, если у этого поиска нет предыдущего класса поиска, M1 — это модуль, содержащий lookupClass, а M2 — это модуль, содержащий targetClass, то targetClass доступен тогда и только тогда, когда

  • M1 читает M2, и
  • targetClass является открытым и находится в пакете, экспортируемом M2 как минимум для M1.

В противном случае, если у этого поиска есть предыдущий класс поиска, M1 и M2 определены как прежде, а M0 — это модуль, содержащий предыдущий класс поиска, то targetClass доступен тогда и только тогда, когда выполняется одно из следующих условий:

  • targetClass находится в M0, и M1 читает Module.canRead(Module) M0, а тип находится в пакете, экспортируемом как минимум для M1.
  • targetClass находится в M1, и M0 читает Module.canRead(Module) M1, а тип находится в пакете, экспортируемом как минимум для M0.
  • targetClass находится в третьем модуле M2, и как M0, так и M1 читают M2, а тип находится в пакете, экспортируемом как минимум для обоих модулей: M0 и M2.

В противном случае targetClass недоступен.

Параметры типа:
T — тип класса, для которого проверяется доступ
Параметры:
targetClass — класс, для которого проверяется доступ
Возвращает:
targetClass, для которого выполнена проверка доступа
Исключения:
IllegalAccessException — если класс недоступен из класса поиска и предыдущего класса поиска, если он присутствует, в разрешённых режимах доступа.
NullPointerException — если targetClass равно null
Начиная с версии:
9
См. также:
  • Поиск между модулями

findSpecial

public MethodHandle findSpecial(Class<?> refc, String name, MethodType type, Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException
Создаёт раннесвязанный дескриптор метода для виртуального метода. Он обходит проверки на переопределённые методы у получателя, как если бы вызов выполнялся инструкцией invokespecial из явно указанного specialCaller. Тип дескриптора метода будет типом метода с добавленным в начало подходящим ограниченным типом получателя. (Тип получателя будет specialCaller или его подтипом.) Метод и все его типы аргументов должны быть доступны объекту поиска.

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

Возвращённый дескриптор метода будет иметь переменную арность тогда и только тогда, когда установлен бит-модификатор переменной арности метода (0x0080).

(Примечание. Внутренние методы JVM с именем "<init>" недоступны через этот API, хотя инструкция invokespecial может ссылаться на них в особых случаях. Используйте findConstructor для безопасного доступа к методам инициализации экземпляров.)

Пример:

import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
...
static class Listie extends ArrayList {
  public String toString() { return "[wee Listie]"; }
  static Lookup lookup() { return MethodHandles.lookup(); }
}
...
// no access to constructor via invokeSpecial:
MethodHandle MH_newListie = Listie.lookup()
  .findConstructor(Listie.class, methodType(void.class));
Listie l = (Listie) MH_newListie.invokeExact();
try { assertEquals("impossible", Listie.lookup().findSpecial(
        Listie.class, "<init>", methodType(void.class), Listie.class));
 } catch (NoSuchMethodException ex) { } // OK
// access to super and self methods via invokeSpecial:
MethodHandle MH_super = Listie.lookup().findSpecial(
  ArrayList.class, "toString" , methodType(String.class), Listie.class);
MethodHandle MH_this = Listie.lookup().findSpecial(
  Listie.class, "toString" , methodType(String.class), Listie.class);
MethodHandle MH_duper = Listie.lookup().findSpecial(
  Object.class, "toString" , methodType(String.class), Listie.class);
assertEquals("[]", (String) MH_super.invokeExact(l));
assertEquals(""+l, (String) MH_this.invokeExact(l));
assertEquals("[]", (String) MH_duper.invokeExact(l)); // ArrayList method
try { assertEquals("inaccessible", Listie.lookup().findSpecial(
        String.class, "toString", methodType(String.class), Listie.class));
 } catch (IllegalAccessException ex) { } // OK
Listie subl = new Listie() { public String toString() { return "[subclass]"; } };
assertEquals(""+l, (String) MH_this.invokeExact(subl)); // Listie method
Параметры:
refc — класс или интерфейс, из которого вызывается метод
name — имя метода (не должно быть "<init>")
type — тип метода без аргумента-получателя
specialCaller — предполагаемый вызывающий класс для выполнения invokespecial
Возвращает:
требуемый дескриптор метода
Исключения:
NoSuchMethodException — если метод не существует
IllegalAccessException — если проверка доступа завершается неудачно, метод является static или установлен бит-модификатор переменной арности метода, но asVarargsCollector завершается неудачно
NullPointerException — если какой-либо аргумент равен null

findGetter

public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException
Создаёт дескриптор метода, предоставляющий доступ на чтение к нестатическому полю. Тип возвращаемого значения дескриптора метода будет совпадать с типом значения поля. Единственным аргументом дескриптора метода будет экземпляр, содержащий поле. Проверка доступа немедленно выполняется от имени класса поиска.
Параметры:
refc — класс или интерфейс, из которого вызывается метод
name — имя поля
type — тип поля
Возвращает:
дескриптор метода, который может считывать значения из поля
Исключения:
NoSuchFieldException — если поле не существует
IllegalAccessException — если проверка доступа завершается неудачно или поле является static
NullPointerException — если какой-либо аргумент равен null
См. также:
  • findVarHandle(Class, String, Class)

findSetter

public MethodHandle findSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException
Создаёт дескриптор метода, предоставляющий доступ на запись к нестатическому полю. Тип возвращаемого значения дескриптора метода будет void. Дескриптор метода принимает два аргумента: экземпляр, содержащий поле, и записываемое значение. Тип второго аргумента совпадает с типом значения поля. Проверка доступа немедленно выполняется от имени класса поиска.
Параметры:
refc — класс или интерфейс, из которого вызывается метод
name — имя поля
type — тип поля
Возвращает:
дескриптор метода, который может записывать значения в поле
Исключения:
NoSuchFieldException — если поле не существует
IllegalAccessException — если проверка доступа завершается неудачно или поле является static или final
NullPointerException — если какой-либо аргумент равен null
См. также:
  • findVarHandle(Class, String, Class)

findVarHandle

public VarHandle findVarHandle(Class<?> recv, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException
Создаёт VarHandle, предоставляющий доступ к нестатическому полю name типа type, объявленному в классе типа recv. Типом переменной VarHandle является type; у него один координатный тип — recv.

Проверка доступа немедленно выполняется от имени класса поиска.

При следующих условиях некоторые режимы доступа возвращённого VarHandle не поддерживаются:

  • если поле объявлено как final, то режимы доступа для записи, атомарного обновления, числового атомарного обновления и побитового атомарного обновления не поддерживаются.
  • если тип поля не является byte, short, char, int, long, float или double, то режимы доступа для числового атомарного обновления не поддерживаются.
  • если тип поля не является boolean, byte, short, char, int или long, то режимы доступа для побитового атомарного обновления не поддерживаются.

Если поле объявлено как volatile, возвращённый VarHandle переопределяет доступ к полю (фактически игнорируя объявление volatile) в соответствии с указанными для него режимами доступа.

Если тип поля — float или double, режимы доступа для числового и атомарного обновления сравнивают значения по их побитовому представлению (см. соответственно Float.floatToRawIntBits(float) и Double.doubleToRawLongBits(double)).

Примечание к API:
Побитовое сравнение значений float или double, выполняемое режимами доступа для числового и атомарного обновления, отличается от примитивного оператора == и методов Float.equals(Object) и Double.equals(Object), в частности при сравнении значений NaN или сравнении -0.0 с +0.0. При выполнении операций compare-and-set или compare-and-exchange с такими значениями следует соблюдать осторожность, поскольку операция может неожиданно завершиться неудачно. В Java существует множество возможных значений NaN, которые считаются NaN, хотя ни одна предоставляемая Java операция с плавающей точкой IEEE 754 не может различить их. Операция может завершиться неудачно, если ожидаемое значение или значение-свидетель является NaN и преобразуется (возможно, платформозависимым способом) в другое значение NaN, вследствие чего его побитовое представление меняется (подробности см. в Float.intBitsToFloat(int) или Double.longBitsToDouble(long)). Значения -0.0 и +0.0 имеют разные побитовые представления, но считаются равными при использовании примитивного оператора ==. Операция может завершиться неудачно, если, например, числовой алгоритм вычислил ожидаемое значение, скажем, -0.0, а ранее вычислил значение-свидетель, скажем, +0.0.
Параметры:
recv — класс-получатель типа R, объявляющий нестатическое поле
name — имя поля
type — тип поля, типа T
Возвращает:
VarHandle, предоставляющий доступ к нестатическим полям.
Исключения:
NoSuchFieldException — если поле не существует
IllegalAccessException — если проверка доступа завершается неудачно или поле является static
NullPointerException — если какой-либо аргумент равен null
Начиная с версии:
9

findStaticGetter

public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException
Создаёт дескриптор метода, предоставляющий доступ на чтение к статическому полю. Тип возвращаемого значения дескриптора метода будет совпадать с типом значения поля. Дескриптор метода не принимает аргументов. Проверка доступа немедленно выполняется от имени класса поиска.

При вызове возвращённого дескриптора метода класс поля будет инициализирован, если он ещё не был инициализирован.

Параметры:
refc — класс или интерфейс, из которого вызывается метод
name — имя поля
type — тип поля
Возвращает:
дескриптор метода, который может считывать значения из поля
Исключения:
NoSuchFieldException — если поле не существует
IllegalAccessException — если проверка доступа завершается неудачно или поле не является static
NullPointerException — если какой-либо аргумент равен null

findStaticSetter

public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException
Создаёт дескриптор метода, предоставляющий доступ на запись к статическому полю. Тип возвращаемого значения дескриптора метода будет void. Дескриптор метода принимает один аргумент — записываемое значение типа значения поля. Проверка доступа немедленно выполняется от имени класса поиска.

При вызове возвращённого дескриптора метода класс поля будет инициализирован, если он ещё не был инициализирован.

Параметры:
refc — класс или интерфейс, из которого вызывается метод
name — имя поля
type — тип поля
Возвращает:
дескриптор метода, который может записывать значения в поле
Исключения:
NoSuchFieldException — если поле не существует
IllegalAccessException — если проверка доступа завершается неудачно, поле не является static или является final
NullPointerException — если какой-либо аргумент равен null

findStaticVarHandle

public VarHandle findStaticVarHandle(Class<?> decl, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException
Создаёт VarHandle, предоставляющий доступ к статическому полю name типа type, объявленному в классе типа decl. Типом переменной VarHandle является type; у него нет координатных типов.

Проверка доступа немедленно выполняется от имени класса поиска.

При выполнении операций с возвращённым VarHandle объявляющий класс будет инициализирован, если он ещё не был инициализирован.

При следующих условиях некоторые режимы доступа возвращённого VarHandle не поддерживаются:

  • если поле объявлено как final, то режимы доступа для записи, атомарного обновления, числового атомарного обновления и побитового атомарного обновления не поддерживаются.
  • если тип поля не является byte, short, char, int, long, float или double, то режимы доступа для числового атомарного обновления не поддерживаются.
  • если тип поля не является boolean, byte, short, char, int или long, то режимы доступа для побитового атомарного обновления не поддерживаются.

Если поле объявлено как volatile, возвращённый VarHandle переопределяет доступ к полю (фактически игнорируя объявление volatile) в соответствии с указанными для него режимами доступа.

Если тип поля — float или double, режимы доступа для числового и атомарного обновления сравнивают значения по их побитовому представлению (см. соответственно Float.floatToRawIntBits(float) и Double.doubleToRawLongBits(double)).

Примечание к API:
Побитовое сравнение значений float или double, выполняемое режимами доступа для числового и атомарного обновления, отличается от примитивного оператора == и методов Float.equals(Object) и Double.equals(Object), в частности при сравнении значений NaN или сравнении -0.0 с +0.0. При выполнении операций compare-and-set или compare-and-exchange с такими значениями следует соблюдать осторожность, поскольку операция может неожиданно завершиться неудачно. В Java существует множество возможных значений NaN, которые считаются NaN, хотя ни одна предоставляемая Java операция с плавающей точкой IEEE 754 не может различить их. Операция может завершиться неудачно, если ожидаемое значение или значение-свидетель является NaN и преобразуется (возможно, платформозависимым способом) в другое значение NaN, вследствие чего его побитовое представление меняется (подробности см. в Float.intBitsToFloat(int) или Double.longBitsToDouble(long)). Значения -0.0 и +0.0 имеют разные побитовые представления, но считаются равными при использовании примитивного оператора ==. Операция может завершиться неудачно, если, например, числовой алгоритм вычислил ожидаемое значение, скажем, -0.0, а ранее вычислил значение-свидетель, скажем, +0.0.
Параметры:
decl — класс, объявляющий статическое поле
name — имя поля
type — тип поля, типа T
Возвращает:
VarHandle, предоставляющий доступ к статическому полю
Исключения:
NoSuchFieldException — если поле не существует
IllegalAccessException — если проверка доступа завершается неудачно или поле не является static
NullPointerException — если какой-либо аргумент равен null
Начиная с версии:
9

bind

public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException
Создаёт раннесвязанный дескриптор метода для нестатического метода. Получатель должен иметь надтип defc, в котором метод с заданным именем и типом доступен классу поиска. Метод и все его типы аргументов должны быть доступны объекту поиска. Тип дескриптора метода будет типом метода без добавления дополнительного параметра-получателя. Указанный получатель будет связан с дескриптором метода, поэтому каждый вызов дескриптора будет вызывать запрошенный метод для этого получателя.

Возвращённый дескриптор метода будет иметь переменную арность тогда и только тогда, когда установлен бит-модификатор переменной арности метода (0x0080) и завершающий аргумент-массив не является единственным аргументом. (Если завершающий аргумент-массив является единственным аргументом, к нему будет привязано значение заданного получателя.)

Это почти эквивалентно следующему коду, за исключением различий, перечисленных ниже:

import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
...
MethodHandle mh0 = lookup().findVirtual(defc, name, type);
MethodHandle mh1 = mh0.bindTo(receiver);
mh1 = mh1.withVarargs(mh0.isVarargsCollector());
return mh1;
где defc — это либо receiver.getClass(), либо надтип этого класса, в котором запрошенный метод доступен классу поиска. (В отличие от bind, bindTo не сохраняет переменную арность. Кроме того, bindTo может выбросить ClassCastException в тех случаях, когда bind выбросил бы IllegalAccessException, например, если член является protected, а получатель ограничен findVirtual классом поиска.)
Параметры:
receiver — объект, из которого вызывается метод
name — имя метода
type — тип метода без аргумента-получателя
Возвращает:
требуемый дескриптор метода
Исключения:
NoSuchMethodException — если метод не существует
IllegalAccessException — если проверка доступа завершается неудачно или установлен бит-модификатор переменной арности метода, но asVarargsCollector завершается неудачно
NullPointerException — если какой-либо аргумент равен null
См. также:
  • MethodHandle.bindTo(Object)
  • findVirtual(Class, String, MethodType)

unreflect

public MethodHandle unreflect(Method m) throws IllegalAccessException
Создаёт прямой дескриптор метода для m, если класс поиска имеет необходимые права. Если m нестатический, аргумент-получатель рассматривается как начальный аргумент. Если m виртуальный, при каждом вызове учитывается переопределение. В отличие от API Core Reflection, исключения не оборачиваются. Тип дескриптора метода будет типом метода с добавленным в начало типом получателя (только если метод нестатический). Если флаг accessible метода не установлен, проверка доступа немедленно выполняется от имени класса поиска. Если m не является открытым, не передавайте полученный дескриптор ненадёжным сторонам.

Возвращённый дескриптор метода будет иметь переменную арность тогда и только тогда, когда установлен бит-модификатор переменной арности метода (0x0080).

Если m является статическим и возвращённый дескриптор метода вызывается, класс метода будет инициализирован, если он ещё не был инициализирован.

Параметры:
m — отражённый метод
Возвращает:
дескриптор метода, который может вызвать отражённый метод
Исключения:
IllegalAccessException — если проверка доступа завершается неудачно или установлен бит-модификатор переменной арности метода, но asVarargsCollector завершается неудачно
NullPointerException — если аргумент равен null

unreflectSpecial

public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException
Создаёт дескриптор метода для отражённого метода. Он обходит проверки на переопределённые методы у получателя, как если бы вызов выполнялся инструкцией invokespecial из явно указанного specialCaller. Тип дескриптора метода будет типом метода с добавленным в начало подходящим ограниченным типом получателя. (Тип получателя будет specialCaller или его подтипом.) Если флаг accessible метода не установлен, проверка доступа немедленно выполняется от имени класса поиска, как если бы выполнялось связывание инструкции invokespecial.

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

Возвращённый дескриптор метода будет иметь переменную арность тогда и только тогда, когда установлен бит-модификатор переменной арности метода (0x0080).

Параметры:
m — отражённый метод
specialCaller — класс, который предположительно вызывает метод
Возвращает:
дескриптор метода, который может вызвать отражённый метод
Исключения:
IllegalAccessException — если проверка доступа завершается неудачно, метод является static или установлен бит-модификатор переменной арности метода, но asVarargsCollector завершается неудачно
NullPointerException — если какой-либо аргумент равен null

unreflectConstructor

public MethodHandle unreflectConstructor(Constructor<?> c) throws IllegalAccessException
Создает дескриптор метода для отраженного конструктора. Тип дескриптора метода будет соответствовать типу конструктора, но тип возвращаемого значения будет заменен на объявляющий класс. Дескриптор метода выполнит операцию newInstance, создав новый экземпляр класса конструктора на основе аргументов, переданных дескриптору метода.

Если флаг accessible конструктора не установлен, проверка доступа немедленно выполняется от имени класса поиска.

Возвращенный дескриптор метода будет иметь переменное число аргументов тогда и только тогда, когда установлен бит модификатора переменного числа аргументов конструктора (0x0080).

При вызове возвращенного дескриптора метода класс конструктора будет инициализирован, если он еще не был инициализирован.

Параметры:
c — отраженный конструктор
Возвращает:
дескриптор метода, который может вызывать отраженный конструктор
Вызывает исключения:
IllegalAccessException — если проверка доступа завершается неудачей или если установлен бит модификатора переменного числа аргументов метода и asVarargsCollector завершается неудачей
NullPointerException — если аргумент равен null

unreflectGetter

public MethodHandle unreflectGetter(Field f) throws IllegalAccessException
Создает дескриптор метода, предоставляющий доступ для чтения к отраженному полю. Тип возвращаемого значения дескриптора метода будет соответствовать типу значения поля. Если поле является static, дескриптор метода не будет принимать аргументов. В противном случае его единственным аргументом будет экземпляр, содержащий поле. Если флаг accessible объекта Field не установлен, проверка доступа немедленно выполняется от имени класса поиска.

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

Параметры:
f — отраженное поле
Возвращает:
дескриптор метода, который может загружать значения из отраженного поля
Вызывает исключения:
IllegalAccessException — если проверка доступа завершается неудачей
NullPointerException — если аргумент равен null

unreflectSetter

public MethodHandle unreflectSetter(Field f) throws IllegalAccessException
Создает дескриптор метода, предоставляющий доступ для записи к отраженному полю. Дескриптор метода будет иметь тип возвращаемого значения void. Если поле является static, дескриптор метода будет принимать один аргумент типа значения поля — значение, которое нужно сохранить. В противном случае двумя аргументами будут экземпляр, содержащий поле, и значение, которое нужно сохранить. Если флаг accessible объекта Field не установлен, проверка доступа немедленно выполняется от имени класса поиска.

Если поле является final, доступ для записи будет запрещен и проверка доступа завершится неудачей, за исключением определенных узких обстоятельств, описанных для Field.set. Дескриптор метода возвращается только в том случае, если соответствующий вызов метода set объекта Field мог бы завершиться нормально. В частности, поля, которые одновременно являются static и final, никогда не могут быть установлены.

Если поле является static и возвращенный дескриптор метода вызывается, класс поля будет инициализирован, если он еще не был инициализирован.

Параметры:
f — отраженное поле
Возвращает:
дескриптор метода, который может сохранять значения в отраженное поле
Вызывает исключения:
IllegalAccessException — если проверка доступа завершается неудачей или если поле является final и доступ для записи не разрешен для объекта Field
NullPointerException — если аргумент равен null

unreflectVarHandle

public VarHandle unreflectVarHandle(Field f) throws IllegalAccessException
Создает VarHandle, предоставляющий доступ к отраженному полю f типа T, объявленному в классе типа R. Переменный тип VarHandle — T. Если поле нестатическое, VarHandle имеет один координатный тип — R. В противном случае поле является статическим, и VarHandle не имеет координатных типов.

Проверка доступа немедленно выполняется от имени класса поиска независимо от значения флага accessible поля.

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

При следующих условиях некоторые режимы доступа возвращенного VarHandle не поддерживаются:

  • если поле объявлено как final, режимы доступа для записи, атомарного обновления, числового атомарного обновления и побитового атомарного обновления не поддерживаются.
  • если тип поля не является byte, short, char, int, long, float или double, режимы доступа для числового атомарного обновления не поддерживаются.
  • если тип поля не является boolean, byte, short, char, int или long, режимы доступа для побитового атомарного обновления не поддерживаются.

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

Если тип поля — float или double, числовые режимы доступа и режимы доступа атомарного обновления сравнивают значения, используя их побитовое представление (см. Float.floatToRawIntBits(float) и Double.doubleToRawLongBits(double) соответственно).

Примечание к API:
Побитовое сравнение значений float или double, выполняемое числовыми режимами доступа и режимами доступа атомарного обновления, отличается от примитивного оператора ==, а также методов Float.equals(Object) и Double.equals(Object), в частности при сравнении значений NaN или сравнении -0.0 с +0.0. При выполнении операций compare-and-set или compare-and-exchange с такими значениями следует проявлять осторожность, поскольку операция может неожиданно завершиться неудачей. В Java существует множество значений NaN, которые считаются NaN, хотя ни одна операция с плавающей точкой IEEE 754, предоставляемая Java, не может различить их. Операция может завершиться неудачей, если ожидаемое значение или значение-свидетель является NaN и оно преобразуется (возможно, специфичным для платформы способом) в другое значение NaN, имеющее иное побитовое представление (подробности см. в Float.intBitsToFloat(int) или Double.longBitsToDouble(long)). Значения -0.0 и +0.0 имеют разные побитовые представления, но считаются равными при использовании примитивного оператора ==. Операция может завершиться неудачей, если, например, числовой алгоритм вычисляет ожидаемое значение, равное, скажем, -0.0, а ранее вычислил значение-свидетель, равное, скажем, +0.0.
Параметры:
f — отраженное поле с полем типа T и объявляющим классом типа R
Возвращает:
VarHandle, предоставляющий доступ к нестатическим полям или статическому полю
Вызывает исключения:
IllegalAccessException — если проверка доступа завершается неудачей
NullPointerException — если аргумент равен null
С версии:
9

revealDirect

public MethodHandleInfo revealDirect(MethodHandle target)
Разбирает прямой дескриптор метода, созданный этим объектом поиска или аналогичным объектом. Выполняются проверки безопасности и доступа, чтобы убедиться, что этот объект поиска может воспроизвести целевой дескриптор метода. Это означает, что разбор может завершиться неудачей, если целевой объект является прямым дескриптором метода, но был создан несвязанным объектом поиска. Такое возможно, если дескриптор метода является чувствительным к вызывающему коду и был создан объектом поиска для другого класса.
Параметры:
target — прямой дескриптор метода для разложения на компоненты символьной ссылки
Возвращает:
символьную ссылку, которую можно использовать для восстановления этого дескриптора метода с помощью данного объекта поиска
Вызывает исключения:
IllegalArgumentException — если целевой объект не является прямым дескриптором метода или проверка доступа завершается неудачей
NullPointerException — если целевой объект является null
С версии:
1.8
См. также:
  • MethodHandleInfo

hasPrivateAccess

@Deprecated(since="14") public boolean hasPrivateAccess()
Устарело.
Изначально этот метод был предназначен для проверки доступа PRIVATE, подразумевающего полный привилегированный доступ, однако с тех пор доступ MODULE стал независимым от доступа PRIVATE. Рекомендуется вместо него вызывать hasFullPrivilegeAccess().
Возвращает true, если этот объект поиска имеет доступ PRIVATE и MODULE.
Возвращает:
true, если этот объект поиска имеет доступ PRIVATE и MODULE.
С версии:
9

hasFullPrivilegeAccess

public boolean hasFullPrivilegeAccess()
Возвращает true, если этот объект поиска имеет полный привилегированный доступ, то есть доступ PRIVATE и MODULE. Объект Lookup должен иметь полный привилегированный доступ, чтобы получать доступ ко всем членам, доступным классу поиска.
Возвращает:
true, если этот объект поиска имеет полный привилегированный доступ.
С версии:
14
См. также:
  • доступ к private-членам и доступ к модулю

Сообщить об ошибке или предложить улучшение
Дополнительную справочную информацию по API и документацию для разработчиков см. в разделе Документация Java SE, содержащем более подробные описания для разработчиков, включая концептуальные обзоры, определения терминов, обходные решения и работающие примеры кода. Другие версии.
Java является товарным знаком или зарегистрированным товарным знаком Oracle и/или ее аффилированных лиц в США и других странах.
Авторское право © 1993, 2025, Oracle и/или ее аффилированные лица, 500 Oracle Parkway, Redwood Shores, CA 94065 USA.
Все права защищены. Использование регулируется условиями лицензии и политикой распространения документации.

© 1993, 2025, Oracle and/or its affiliates. All rights reserved.
Documentation extracted from Debian's OpenJDK Development Kit package.
Licensed under the GNU General Public License, version 2, with the Classpath Exception.
Various third party code in OpenJDK is licensed under different licenses (see Debian package).
Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.
https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/invoke/MethodHandles.Lookup.html

Spec-Zone.ru

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