Spec-Zone.ru › OpenJDK 17

Класс MethodHandles

java.lang.Object
java.lang.invoke.MethodHandles
public class MethodHandles extends Object
Этот класс состоит исключительно из статических методов, которые работают с дескрипторами методов или возвращают их. Они делятся на несколько категорий:
  • Методы поиска, которые помогают создать дескрипторы методов для методов и полей.
  • Комбинаторные методы, которые комбинируют или преобразуют существующие дескрипторы методов в новые.
  • Другие фабричные методы для создания дескрипторов методов, которые эмулируют другие общие операции JVM или шаблоны управления потоком.
Метод поиска, комбинаторный или фабричный метод завершится ошибкой и выбросит IllegalArgumentException если тип создаваемого дескриптора метода будет иметь слишком много параметров.
С момента:
1.7

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

Модификатор и тип Класс Описание
static final class  MethodHandles.Lookup
Объект поиска — это фабрика для создания дескрипторов методов, когда для создания требуется проверка доступа.

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

Модификатор и тип Метод Описание
static MethodHandle arrayConstructor(Class<?> arrayClass)
Создаёт обработчик методов, конструирующий массивы заданного типа, как если бы это делал байткод anewarray.
static MethodHandle arrayElementGetter(Class<?> arrayClass)
Создаёт обработчик методов, предоставляющий чтение элементов массива, как если бы это делал байткод aaload.
static MethodHandle arrayElementSetter(Class<?> arrayClass)
Создаёт обработчик методов, предоставляющий запись элементов массива, как если бы это делал байткод astore.
static VarHandle arrayElementVarHandle(Class<?> arrayClass)
Создаёт VarHandle, предоставляющий доступ к элементам массива типа arrayClass.
static MethodHandle arrayLength(Class<?> arrayClass)
Создаёт обработчик методов, возвращающий длину массива, как если бы это делал байткод arraylength.
static VarHandle byteArrayViewVarHandle(Class<?> viewArrayClass, ByteOrder byteOrder)
Создаёт VarHandle, предоставляющий доступ к элементам массива byte[], рассматриваемого как массив другого примитивного типа, например, int[] или long[].
static VarHandle byteBufferViewVarHandle(Class<?> viewArrayClass, ByteOrder byteOrder)
Создаёт VarHandle, предоставляющий доступ к элементам ByteBuffer, рассматриваемого как массив элементов другого примитивного типа, отличного от byte, например, int[] или long[].
static MethodHandle catchException(MethodHandle target, Class<? extends Throwable> exType, MethodHandle handler)
Создаёт обработчик методов, адаптирующий целевой обработчик методов, выполняя его внутри обработчика исключений.
static <T> T classData(MethodHandles.Lookup caller, String name, Class<T> type)
Возвращает данные класса, связанные с классом поиска заданного объекта поиска caller, или null.
static <T> T classDataAt(MethodHandles.Lookup caller, String name, Class<T> type, int index)
Возвращает элемент по указанному индексу в данных класса, если данные класса, связанные с классом поиска заданного объекта поиска caller lookup object, являются List.
static MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle filter)
Адаптирует целевой обработчик методов путём предварительной обработки подпоследовательности его аргументов с помощью фильтра (другой обработчик методов).
static MethodHandle constant(Class<?> type, Object value)
Создаёт обработчик методов запрошенного возвращаемого типа, который возвращает заданное постоянное значение каждый раз при вызове.
static MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body)
Создаёт цикл, который выполняется заданное количество итераций.
static MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body)
Создаёт цикл, перебирающий диапазон чисел.
static MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred)
Создаёт цикл do-while из инициализатора, тела и предиката.
static MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes)
Создаёт обработчик методов, который отбросит некоторые фиктивные аргументы перед вызовом другого указанного целевого обработчика методов.
static MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes)
Создаёт обработчик методов, который отбросит некоторые фиктивные аргументы перед вызовом другого указанного целевого обработчика методов.
static MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List<Class<?>> newTypes, int pos)
Адаптирует целевой обработчик методов для соответствия заданному списку типов параметров.
static MethodHandle dropReturn(MethodHandle target)
Отбрасывает значение возврата целевого обработчика (если таковое имеется).
static MethodHandle empty(MethodType type)
Создаёт обработчик методов запрошенного типа, который игнорирует любые аргументы, ничего не делает и возвращает соответствующее значение по умолчанию в зависимости от типа возвращаемого значения.
static MethodHandle exactInvoker(MethodType type)
Создаёт специальный обработчик вызова методов, который можно использовать для вызова любого обработчика методов заданного типа, как если бы это делал invokeExact.
static MethodHandle explicitCastArguments(MethodHandle target, MethodType newType)
Создаёт обработчик методов, который адаптирует тип заданного обработчика методов к новому типу путём парной конвертации аргументов и типа возвращаемого значения.
static MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters)
Адаптирует целевой обработчик методов путём предварительной обработки одного или нескольких его аргументов, каждый со своей унарной функцией фильтра, а затем вызывает целевой метод с каждым предварительно обработанным аргументом, заменённым результатом соответствующей функции фильтра.
static MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter)
Адаптирует целевой обработчик методов путём последующей обработки значения возврата (если таковое имеется) с помощью фильтра (другой обработчик методов).
static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner)
Адаптирует целевой обработчик методов путём предварительной обработки некоторых его аргументов, начиная с заданной позиции, а затем вызывает целевой метод с результатом предварительной обработки, вставленным в исходную последовательность аргументов непосредственно перед сложенными аргументами.
static MethodHandle foldArguments(MethodHandle target, MethodHandle combiner)
Адаптирует целевой обработчик методов путём предварительной обработки некоторых его аргументов, а затем вызывает целевой метод с результатом предварительной обработки, вставленным в исходную последовательность аргументов.
static MethodHandle guardWithTest(MethodHandle test, MethodHandle target, MethodHandle fallback)
Создаёт обработчик методов, адаптирующий целевой обработчик методов, защищая его с помощью проверки, обработчика методов, возвращающего значение типа boolean.
static MethodHandle identity(Class<?> type)
Создаёт обработчик методов, возвращающий свой единственный аргумент при вызове.
static MethodHandle insertArguments(MethodHandle target, int pos, Object... values)
Предоставляет целевому обработчику методов один или несколько связанных аргументов до вызова обработчика методов.
static MethodHandle invoker(MethodType type)
Создаёт специальный обработчик вызова методов, который можно использовать для вызова любого обработчика методов, совместимого с заданным типом, как если бы это делал invoke.
static MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body)
Создаёт цикл, перебирающий значения, произведённые Iterator<T>.
static MethodHandles.Lookup lookup()
Возвращает lookup object с полными возможностями для эмуляции всех поддерживаемых байткодовых действий вызывающей стороны.
static MethodHandle loop(MethodHandle[]... clauses)
Создаёт обработчик методов, представляющий цикл с несколькими переменными цикла, которые обновляются и проверяются на каждой итерации.
static MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder)
Создаёт обработчик методов, адаптирующий порядок вызова заданного обработчика методов к новому типу путём переупорядочивания аргументов.
static MethodHandles.Lookup privateLookupIn(Class<?> targetClass, MethodHandles.Lookup caller)
Возвращает объект lookup в целевом классе для эмуляции всех поддерживаемых байткодовых действий, включая доступ к закрытым членам.
static MethodHandles.Lookup publicLookup()
Возвращает lookup object, доверие к которому минимально.
static <T extends Member>
T
reflectAs(Class<T> expected, MethodHandle target)
Выполняет необработанное "распаривание" прямого обработчика методов.
static MethodHandle spreadInvoker(MethodType type, int leadingArgCount)
Создаёт обработчик методов, который вызовет любой обработчик методов заданного type, с заданным количеством хвостовых аргументов, заменённых единственным хвостовым массивом Object[].
static MethodHandle tableSwitch(MethodHandle fallback, MethodHandle... targets)
Создаёт обработчик методов табличного переключения, который может использоваться для переключения между набором целевых обработчиков методов на основе заданного целевого индекса, называемого селектором.
static MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType)
Создаёт обработчик методов, который будет генерировать исключения заданного exType.
static MethodHandle tryFinally(MethodHandle target, MethodHandle cleanup)
Создаёт обработчик методов, который адаптирует обработчик методов target путём обертывания его в блок try-finally.
static MethodHandle varHandleExactInvoker(VarHandle.AccessMode accessMode, MethodType type)
Создаёт специальный обработчик вызова методов, который можно использовать для вызова метода с полиморфным типом доступа в VarHandle, у которого ассоциированный тип доступа совместим с заданным типом.
static MethodHandle varHandleInvoker(VarHandle.AccessMode accessMode, MethodType type)
Создаёт специальный обработчик вызова методов, который можно использовать для вызова метода с полиморфным типом доступа в VarHandle, у которого ассоциированный тип доступа совместим с заданным типом.
static MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body)
Создаёт цикл while из инициализатора, тела и предиката.
static MethodHandle zero(Class<?> type)
Создаёт постоянную ручку метода запрошенного возвращаемого типа, которая каждый раз при вызове возвращает значение по умолчанию для этого типа.

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

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

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

lookup

public static MethodHandles.Lookup lookup()
Возвращает lookup object с полными возможностями для эмуляции всех поддерживаемых поведений байткода вызывающего объекта. Эти возможности включают полные привилегии доступа к вызывающему объекту. Методы-фабрики объекта lookup могут создавать прямые обработчики методов для любого члена, к которому вызывающий объект имеет доступ через байткод, включая защищённые и private поля и методы. Данный объект lookup создаётся исходным классом lookup и имеет установленный бит ORIGINAL. Этот объект lookup представляет собой возможность, которая может быть делегирована надёжным агентам. Не храните его там, где к нему может получить доступ ненадежный код.

Этот метод чувствителен к вызывающему объекту, что означает, что он может возвращать разные значения различным вызывающим объектам.

Возвращает:
объект lookup для вызывающего метода с исходным и полным привилегированным доступом.

publicLookup

public static MethodHandles.Lookup publicLookup()
Возвращает lookup object, который минимально надёжен. У lookup есть UNCONDITIONAL режим. Он может использоваться только для создания обработчиков методов для public членов public классов в пакетах, которые экспортируются безусловно.

Согласно соглашению, класс lookup для данного объекта lookup будет Object.

Замечание API:
Использование Object является стандартной практикой, и поскольку режимы lookup ограничены, особый доступ к внутренностям Object, его пакету или модулю не предоставляется. Этот public объект lookup или другой объект lookup с UNCONDITIONAL режимом предполагает читаемость. Соответственно, класс lookup не используется для определения контекста lookup.

Обсуждение: Класс lookup может быть изменён на любой другой класс C с помощью выражения вида publicLookup().in(C.class). Public объект lookup всегда подлежит проверкам менеджера безопасности. Также он не может получить доступ к методам, чувствительным к вызывающему объекту.

Возвращает:
объект lookup, который минимально надёжен

privateLookupIn

public static MethodHandles.Lookup privateLookupIn(Class<?> targetClass, MethodHandles.Lookup caller) throws IllegalAccessException
Возвращает lookup объекта на целевом классе для эмуляции всех поддерживаемых поведений байткода, включая private доступ. Возвращённый объект lookup может предоставить доступ к классам в модулях и пакетах, а также членам этих классов, за пределами обычных правил доступа Java, вместо этого следуя более либеральным правилам модульного глубокого рефлексирования.

Вызывающему объекту, указанному как объект Lookup в модуле M1 разрешено выполнять глубокое рефлексирование по модулю M2 и пакету целевого класса только в том случае, если выполняются все следующие условия:

  • Если менеджер безопасности существует, вызывается его метод checkPermission для проверки ReflectPermission("suppressAccessChecks") и он должен завершиться без исключений.
  • Объект lookup вызывающего объекта должен иметь полные привилегии доступа. В частности:
    • Объект lookup вызывающего объекта должен иметь режим lookup MODULE. (Это необходимо, так как иначе не было бы способа гарантировать, что исходный создатель lookup был членом любого конкретного модуля, и поэтому любые последующие проверки читаемости и квалифицированных экспортов стали бы неэффективными.)
    • Объект lookup вызывающего объекта должен иметь доступ PRIVATE. (Это необходимо, так как приложение, намеревающееся использовать внутримодульный доступ с помощью MODULE в одиночку, непреднамеренно также позволит глубокое рефлексирование к своему собственному модулю.)
  • Целевой класс должен быть обычным классом, а не примитивным или массивом. (Таким образом, M2 хорошо определён.)
  • Если вызывающий модуль M1 отличается от целевого модуля M2, то должны выполняться оба следующих условия:
    • M1 reads M2.
    • M2 opens пакет, содержащий целевой класс, должен быть по крайней мере M1.

Если какое-либо из вышеперечисленных проверок нарушается, этот метод завершается с исключением.

В противном случае, если M1 и M2 являются одним модулем, этот метод возвращает Lookup на targetClass с полным привилегированным доступом и предыдущим классом lookup.

В противном случае, M1 и M2 являются разными модулями. Этот метод возвращает Lookup на targetClass с записью класса lookup вызывающего объекта в качестве нового предыдущего класса lookup с доступом PRIVATE, но без доступа MODULE.

Полученный Lookup объект не имеет ORIGINAL доступа.

Параметры:
targetClass - целевой класс
caller - объект lookup вызывающего объекта
Возвращает:
объект lookup для целевого класса с private доступом
Исключения:
IllegalArgumentException - если targetClass является примитивным типом, void или классом массива
NullPointerException - если targetClass или caller является null
SecurityException - если менеджер безопасности запретил
IllegalAccessException - если любая из других проверок доступа указана выше, не пройдена
С:
9
См. также:
  • MethodHandles.Lookup.dropLookupMode(int)
  • Внутримодульные lookups

classData

public static <T> T classData(MethodHandles.Lookup caller, String name, Class<T> type) throws IllegalAccessException
Возвращает данные класса, связанные с классом lookup данного caller объекта lookup или null.

Скрытый класс с данными класса может быть создан с помощью вызова Lookup::defineHiddenClassWithClassData. Этот метод заставит выполниться статический инициализатор класса lookup данного caller объекта lookup, если он ещё не был инициализирован.

Скрытые классы, созданные с помощью Lookup::defineHiddenClass, и обычные классы не имеют данных класса. null возвращается, если этот метод вызывается для таких классов.

У режимов lookup для этого lookup должен быть исходный доступ для получения данных класса.

Замечание API:
Этот метод может быть вызван как метод-загрузчик для динамически вычисляемой константы. Например, фреймворк может создать скрытый класс с данными класса, которые могут быть Class или MethodHandle объектом. Данные класса доступны только объекту lookup, созданному исходным вызывающим объектом, но недоступны другим членам того же вложенного набора. Если фреймворк передаёт объекты, чувствительные к безопасности, скрытому классу через данные класса, рекомендуется загружать значение данных класса как динамически вычисляемую константу, а не хранить данные класса в частном статическом поле(ах), доступном другим членам вложенного набора.
Параметры типа:
T - тип для преобразования объекта данных класса
Параметры:
caller - контекст lookup, описывающий класс, выполняющий операцию (обычно стекируется JVM)
name - должен быть ConstantDescs.DEFAULT_NAME ("_")
type - тип данных класса
Возвращает:
значение данных класса, если они присутствуют в классе lookup; в противном случае null
Исключения:
IllegalArgumentException - если имя не "_"
IllegalAccessException - если контекст lookup не имеет исходного доступа
ClassCastException - если данные класса не могут быть преобразованы к указанному type
NullPointerException - если caller или type аргумент является null
См. Спецификацию виртуальной машины Java:
5.5 Инициализация
С:
16
См. также:
  • MethodHandles.Lookup.defineHiddenClassWithClassData(byte[], Object, boolean, Lookup.ClassOption...)
  • classDataAt(Lookup, String, Class, int)

classDataAt

public static <T> T classDataAt(MethodHandles.Lookup caller, String name, Class<T> type, int index) throws IllegalAccessException
Возвращает элемент по указанному индексу в данных класса данных класса, если данные класса, связанные с классом поиска заданного объекта поиска, являются List. Если данные класса отсутствуют в этом классе поиска, этот метод возвращает null.

Скрытый класс с данными класса можно создать, вызвав Lookup::defineHiddenClassWithClassData. Этот метод заставит выполнить статический инициализатор класса поиска заданного caller объекта поиска, если он не был инициализирован.

Скрытый класс, созданный с помощью Lookup::defineHiddenClass, и нескрытые классы не имеют данных класса. null возвращается, если этот метод вызывается для объекта поиска в этих классах.

Режимы поиска для этого поиска должны иметь оригинальный доступ для получения данных класса.

API Note:
Этот метод может вызываться как метод загрузки для динамически вычисляемой константы. Например, фреймворк может создать скрытый класс с данными класса, который может List.of(o1, o2, o3....) содержать более одного объекта, и использовать этот метод для загрузки одного элемента по определенному индексу. Данные класса доступны только объекту поиска, созданному исходным вызывающим объектом, но недоступны другим членам в том же вложенном блоке. Если фреймворк передает объекты, чувствительные к безопасности, в скрытый класс через данные класса, рекомендуется загружать значение данных класса как динамически вычисляемую константу вместо хранения данных класса в частном(ых) статическом(их) поле(ях), которые доступны другим вложенным элементам.
Type Parameters:
T - тип для преобразования результата объекта
Parameters:
caller - контекст поиска, описывающий класс, выполняющий операцию (обычно стекируется JVM)
name - должен быть ConstantDescs.DEFAULT_NAME ("_")
type - тип элемента по указанному индексу в данных класса
index - индекс элемента в данных класса
Returns:
элемент по указанному индексу в данных класса, если данные класса присутствуют; в противном случае null
Throws:
IllegalArgumentException - если имя не "_"
IllegalAccessException - если контекст поиска не имеет оригинального доступа
ClassCastException - если данные класса нельзя преобразовать в List или элемент по указанному индексу нельзя преобразовать в заданный тип
IndexOutOfBoundsException - если индекс выходит за пределы диапазона
NullPointerException - если caller или type аргумент null; или если операция unboxing завершается неудачно, потому что элемент по указанному индексу null
Since:
16
See Also:
  • classData(Lookup, String, Class)
  • MethodHandles.Lookup.defineHiddenClassWithClassData(byte[], Object, boolean, Lookup.ClassOption...)

reflectAs

public static <T extends Member> T reflectAs(Class<T> expected, MethodHandle target)
Выполняет необработанное "раскрытие" прямого обработчика метода. Результат такой, как если бы пользователь получил объект поиска, достаточно способный для раскрытия целевого обработчика метода, вызвав Lookup.revealDirect для целевого метода, чтобы получить его символическую ссылку, а затем вызвав MethodHandleInfo.reflectAs для разрешения символической ссылки на член.

Если есть менеджер безопасности, вызывается его метод checkPermission с разрешением ReflectPermission("suppressAccessChecks")

Type Parameters:
T - желаемый тип результата, либо Member, или подтип
Parameters:
target - прямой обработчик метода для раскрытия в компоненты символической ссылки
expected - объект класса, представляющий желаемый тип результата T
Returns:
ссылка на объект метода, конструктора или поля
Throws:
SecurityException - если вызывающий объект не обладает привилегиями для вызова setAccessible
NullPointerException - если любой из аргументов null
IllegalArgumentException - если целевой объект не является прямым обработчиком метода
ClassCastException - если член не является ожидаемого типа
Since:
1.8

arrayConstructor

public static MethodHandle arrayConstructor(Class<?> arrayClass) throws IllegalArgumentException
Создает обработчик метода, создающий массивы желаемого типа, как если бы он был создан anewarray байткодом. Тип возвращаемого обработчика метода будет типом массива. Тип единственного аргумента будет int, который указывает размер массива.

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

Parameters:
arrayClass - тип массива
Returns:
обработчик метода, который может создавать массивы заданного типа
Throws:
NullPointerException - если аргумент null
IllegalArgumentException - если arrayClass не является типом массива
See Java Virtual Machine Specification:
6.5 anewarray Инструкция
Since:
9
See Also:
  • Array.newInstance(Class, int)

arrayLength

public static MethodHandle arrayLength(Class<?> arrayClass) throws IllegalArgumentException
Создает обработчик метода, возвращающий длину массива, как если бы это было сделано arraylength байткодом. Тип обработчика метода будет иметь int как тип возвращаемого значения, а единственный аргумент будет типом массива.

Если возвращаемый обработчик метода вызывается с null ссылкой на массив, будет брошено NullPointerException

Parameters:
arrayClass - тип массива
Returns:
обработчик метода, который может получить длину массива заданного типа массива
Throws:
NullPointerException - если аргумент null
IllegalArgumentException - если arrayClass не является типом массива
See Java Virtual Machine Specification:
6.5 arraylength Инструкция
Since:
9

arrayElementGetter

public static MethodHandle arrayElementGetter(Class<?> arrayClass) throws IllegalArgumentException
Создает обработчик метода, предоставляющий доступ для чтения к элементам массива, как если бы это было сделано aaload байткодом. Тип обработчика метода будет иметь тип элемента массива в качестве типа возвращаемого значения. Первый аргумент будет типом массива, а второй - int.

При вызове возвращаемого обработчика метода ссылка на массив и индекс массива проверяются. Будет брошено NullPointerException, если ссылка на массив null, и ArrayIndexOutOfBoundsException будет брошено, если индекс отрицательный или если он больше или равен длине массива.

Parameters:
arrayClass - тип массива
Returns:
обработчик метода, который может загружать значения из данного типа массива
Throws:
NullPointerException - если аргумент null
IllegalArgumentException - если arrayClass не является типом массива
See Java Virtual Machine Specification:
6.5 aaload Инструкция

arrayElementSetter

public static MethodHandle arrayElementSetter(Class<?> arrayClass) throws IllegalArgumentException
Создает обработчик метода, предоставляющий доступ для записи к элементам массива, как если бы это было сделано astore байткодом. Тип обработчика метода будет иметь тип возврата void. Последний аргумент будет типом элемента массива. Первые два аргумента будут типом массива и int.

При вызове возвращаемого обработчика метода ссылка на массив и индекс массива проверяются. Будет брошено NullPointerException, если ссылка на массив null, и ArrayIndexOutOfBoundsException будет брошено, если индекс отрицательный или если он больше или равен длине массива.

Parameters:
arrayClass - класс массива
Returns:
обработчик метода, который может сохранять значения в массив данного типа
Throws:
NullPointerException - если аргумент null
IllegalArgumentException - если arrayClass не является типом массива
See Java Virtual Machine Specification:
6.5 aastore Инструкция

Обработка элементов массива

public static VarHandle arrayElementVarHandle(Class<?> arrayClass) throws IllegalArgumentException
Создаёт VarHandle для доступа к элементам массива типа arrayClass. Тип переменной VarHandle — это компонентный тип arrayClass, а список типов координат — (arrayClass, int), где тип координаты int соответствует аргументу, являющемуся индексом в массиве.

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

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

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

При вызове возвращаемого VarHandle, ссылка на массив и индекс проверяются. Будет брошено исключение NullPointerException, если ссылка на массив null, и исключение ArrayIndexOutOfBoundsException, если индекс отрицательный или больше или равен длине массива.

Примечание API:
Битовое сравнение значений float или значений double, выполняемое режимами числового и атомарного обновления, отличается от оператора примитивного == и методов Float.equals(java.lang.Object) и Double.equals(java.lang.Object), особенно в отношении сравнения значений NaN или сравнения -0.0 с +0.0. Следует проявлять осторожность при выполнении операций сравнения и установки или сравнения и обмена с такими значениями, так как операция может неожиданно завершиться неудачно. Существует множество возможных значений NaN, которые считаются NaN в Java, хотя ни одна операция с плавающей точкой IEEE 754, предоставляемая Java, не может отличить их друг от друга. Отказ от выполнения операции может произойти, если ожидаемое или наблюдаемое значение является значением NaN, и оно преобразуется (возможно, платформозависимым образом) в другое значение NaN и, следовательно, имеет другое битовое представление (см. Float.intBitsToFloat(int) или Double.longBitsToDouble(long) для получения дополнительной информации). Значения -0.0 и +0.0 имеют разные битовые представления, но считаются равными при использовании оператора примитивного ==. Отказ от выполнения операции может произойти, если, например, числовой алгоритм вычислит ожидаемое значение, скажем, -0.0, и ранее вычислил наблюдаемое значение, скажем, +0.0.
Параметры:
arrayClass - класс массива типа T[]
Возвращает:
VarHandle для доступа к элементам массива
Исключение:
NullPointerException - если arrayClass равен null
IllegalArgumentException - если arrayClass не является типом массива
С:
9

Просмотр массива байтов

public static VarHandle byteArrayViewVarHandle(Class<?> viewArrayClass, ByteOrder byteOrder) throws IllegalArgumentException
Создаёт VarHandle для доступа к элементам массива byte[] байтов, как будто это массив другого примитивного типа, например, int[] или long[]. Тип переменной VarHandle — это компонентный тип viewArrayClass, а список типов координат — (byte[], int), где тип координаты int соответствует аргументу, являющемуся индексом в массиве byte[] байтов. Возвращаемая VarHandle обращается к байтам по индексу в массиве byte[] байтов, комбинируя байты с или из значения компонентного типа viewArrayClass в соответствии с заданным порядком байтов.

Поддерживаемые компонентные типы (типы переменных) — short, char, int, long, float и double.

Доступ к байтам по заданному индексу приведёт к исключению ArrayIndexOutOfBoundsException, если индекс меньше 0 или больше, чем длина массива byte[] минус размер (в байтах) T.

Доступ к байтам по индексу может быть выровнен или не выровнен для T, относительно базового адрес памяти A, связанного с массивом и индексом. Если доступ не выровнен, доступ к любым режимам, кроме get и set, приведёт к исключению IllegalStateException. В таких случаях атомарный доступ гарантирован только относительно наибольшей степени двойки, которая делит НОД A и размер (в байтах) T. Если доступ выровнен, то следующие режимы доступа поддерживаются и гарантируют атомарный доступ:

  • режимы чтения и записи для всех T, за исключением режимов доступа get и set для long и double на 32-битных платформах.
  • режимы атомарного обновления для int, long, float или double (будущие крупные обновления платформы JDK могут добавить поддержку дополнительных типов для некоторых режимов доступа, которые в настоящее время не поддерживаются).
  • режимы числового атомарного обновления для int и long (будущие крупные обновления платформы JDK могут добавить поддержку дополнительных числовых типов для некоторых режимов доступа, которые в настоящее время не поддерживаются).
  • режимы битового атомарного обновления для int и long (будущие крупные обновления платформы JDK могут добавить поддержку дополнительных числовых типов для некоторых режимов доступа, которые в настоящее время не поддерживаются).

Невыровненный доступ, а следовательно, гарантии атомарности, могут быть определены для массивов byte[] без работы с конкретным массивом. Учитывая index, T и соответствующий упакованный тип T_BOX, невыравнивание можно определить следующим образом:


 int sizeOfT = T_BOX.BYTES;  // size in bytes of T
 int misalignedAtZeroIndex = ByteBuffer.wrap(new byte[0]).
     alignmentOffset(0, sizeOfT);
 int misalignedAtIndex = (misalignedAtZeroIndex + index) % sizeOfT;
 boolean isMisaligned = misalignedAtIndex != 0;
 

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

Параметры:
viewArrayClass - класс массива представления, с компонентным типом T
byteOrder - порядок байтов элементов массива представления, хранимых в базовом массиве byte
Возвращает:
VarHandle для доступа к элементам массива byte[] представления, как будто они соответствуют компонентному типу класса массива представления
Исключение:
NullPointerException - если viewArrayClass или byteOrder равны null
IllegalArgumentException - если viewArrayClass не является типом массива
UnsupportedOperationException - если компонентный тип viewArrayClass не поддерживается как тип переменной
С:
9

byteBufferViewVarHandle

public static VarHandle byteBufferViewVarHandle(Class<?> viewArrayClass, ByteOrder byteOrder) throws IllegalArgumentException
Создаёт VarHandle, предоставляющий доступ к элементам ByteBuffer, рассматриваемым как массив элементов другого примитивного типа, отличного от типа byte, например, int[] или long[]. Тип переменной VarHandle — это тип компонента viewArrayClass, а список типов координат — (ByteBuffer, int), где тип координаты int соответствует аргументу, являющемуся индексом в массиве byte[]. Возвращаемое VarHandle обращается к байтам по индексу в ByteBuffer, собирая байты в значение типа компонента viewArrayClass в соответствии с заданным порядком байтов.

Поддерживаемые типы компонентов (типы переменных) — short, char, int, long, float и double.

Доступ приведёт к ReadOnlyBufferException для операций, отличных от чтения, если ByteBuffer является только для чтения.

Доступ к байтам по заданному индексу приведёт к IndexOutOfBoundsException, если индекс меньше 0 или больше, чем предел ByteBuffer минус размер (в байтах) T.

Доступ к байтам по индексу может быть выровненным или невыровненным относительно базового адреса памяти, A скажем, связанного с ByteBuffer и индексом. Если доступ невыровнен, то доступ для операций, отличных от get и set режимов доступа, приведёт к IllegalStateException. В таких случаях атомный доступ гарантируется только по отношению к наибольшей степени двойки, которая делит НОД A и размера (в байтах) T. Если доступ выровнен, то поддерживаются следующие режимы доступа, которые гарантированно поддерживают атомный доступ:

  • режимы доступа для чтения и записи для всех T, за исключением режимов доступа get и set для long и double на 32-битных платформах.
  • атомные режимы обновления для int, long, float или double. (В будущих крупных выпусках платформы JDK могут быть добавлены дополнительные типы для некоторых в настоящее время неподдерживаемых режимов доступа.)
  • атомные режимы числового обновления для int и long. (В будущих крупных выпусках платформы JDK могут быть добавлены дополнительные числовые типы для некоторых в настоящее время неподдерживаемых режимов доступа.)
  • атомные режимы побитового обновления для int и long. (В будущих крупных выпусках платформы JDK могут быть добавлены дополнительные числовые типы для некоторых в настоящее время неподдерживаемых режимов доступа.)

Невыровненные операции доступа, и, следовательно, гарантии атомности, могут быть определены для ByteBuffer, bb (прямого или иного), index, T и его соответствующего упакованного типа, T_BOX, следующим образом:


 int sizeOfT = T_BOX.BYTES;  // size in bytes of T
 ByteBuffer bb = ...
 int misalignedAtIndex = bb.alignmentOffset(index, sizeOfT);
 boolean isMisaligned = misalignedAtIndex != 0;
 

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

Parameters:
viewArrayClass - класс массива представления, с типом компонента типа T
byteOrder - порядок байтов элементов массива представления, как хранится в базовом ByteBuffer (Обратите внимание, что это переопределяет порядок байтов ByteBuffer)
Returns:
VarHandle, предоставляющий доступ к элементам ByteBuffer как будто элементы соответствуют типу компонентов класса массива представления
Throws:
NullPointerException - если viewArrayClass или byteOrder равны null
IllegalArgumentException - если viewArrayClass не является типом массива
UnsupportedOperationException - если тип компонента viewArrayClass не поддерживается как тип переменной
Since:
9

spreadInvoker

public static MethodHandle spreadInvoker(MethodType type, int leadingArgCount)
Создаёт обработчик методов, который будет вызывать любой обработчик методов данного type, заменяя указанное количество хвостовых аргументов единственным хвостовым массивом Object[]. Результирующий обработчик вызовов будет обработчиком методов со следующими аргументами:
  • один целевой MethodHandle
  • ноль или более ведущих значений (подсчитывается leadingArgCount)
  • массив Object[] содержащий хвостовые аргументы

Обработчик вызовов вызовет свою цель как вызов invoke с указанными type. То есть, если цель точно соответствует заданному type, она будет вести себя как invokeExact; в противном случае она будет вести себя так, как если бы использовался asType для преобразования цели к требуемому type.

Тип возвращаемого обработчика вызовов не будет заданным type, а будет содержать все параметры, кроме первого leadingArgCount, заменённые единственным массивом типа Object[], который будет последним параметром.

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

Этот метод эквивалентен следующему коду (хотя может быть более эффективным):


MethodHandle invoker = MethodHandles.invoker(type);
int spreadArgCount = type.parameterCount() - leadingArgCount;
invoker = invoker.asSpreader(Object[].class, spreadArgCount);
return invoker;
 
Этот метод не генерирует исключения, связанные с рефлексией или безопасностью.
Parameters:
type - желаемый тип цели
leadingArgCount - количество фиксированных аргументов, которые будут переданы цели без изменений
Returns:
обработчик методов, подходящий для вызова любого обработчика методов заданного типа
Throws:
NullPointerException - если type равно null
IllegalArgumentException - если leadingArgCount не находится в диапазоне от 0 до type.parameterCount() включительно, или если у результирующего обработчика методов будет слишком много параметров слишком много параметров

exactInvoker

public static MethodHandle exactInvoker(MethodType type)
Создаёт специальный обработчик вызовов метод-обработчика, который может использоваться для вызова любого обработчика методов данного типа, как если бы он был вызван invokeExact. Результирующий обработчик вызовов будет иметь тип, точно равный желаемому типу, за исключением того, что он будет принимать дополнительный ведущий аргумент типа MethodHandle.

Этот метод эквивалентен следующему коду (хотя может быть более эффективным): publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)

Обсуждение: Обработчики вызовов могут быть полезны при работе с изменяемыми обработчиками методов неизвестных типов. Например, чтобы эмулировать вызов invokeExact к переменной методу-обработчику M, извлечь его тип T, найти обработчик вызовов X для T, и вызвать обработчик вызовов, как X.invoke(T, A...). (Нельзя вызвать X.invokeExact, так как тип T неизвестен.) Если требуется распределение, сбор или другие преобразования аргументов, их можно применить один раз к обработчику вызовов X и повторно использовать для многих значений обработчика методов M, при условии, что они совместимы с типом X.

(Примечание: обработчик вызовов не доступен через API Core Reflection. Попытка вызвать java.lang.reflect.Method.invoke на объявленный invokeExact или invoke метод вызовет UnsupportedOperationException.)

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

Parameters:
type - желаемый тип цели
Returns:
обработчик методов, подходящий для вызова любого обработчика методов заданного типа
Throws:
IllegalArgumentException - если у результирующего обработчика методов будет слишком много параметров слишком много параметров

invoker

public static MethodHandle invoker(MethodType type)
Производит специальную обработчик-метод, который может использоваться для вызова любого обработчика-метода, совместимого с заданным типом, как если бы это делалось с помощью invoke. Результирующий обработчик-метод будет иметь тип, точно равный желаемому типу, за исключением того, что он будет принимать дополнительный ведущий аргумент типа MethodHandle.

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

Этот метод эквивалентен следующему коду (хотя он может быть более эффективным): publicLookup().findVirtual(MethodHandle.class, "invoke", type)

Обсуждение: общий тип метода — это тип, который упоминает только Object аргументы и возвращаемые значения. Обработчик-метод для такого типа способен вызывать любой обработчик-метод с такой же арностью, как и общий тип.

(Примечание: метод обработчика недоступен через API Core Reflection. Попытка вызвать java.lang.reflect.Method.invoke на объявленном invokeExact или invoke методе приведет к возбуждению UnsupportedOperationException.)

Этот метод не вызывает рефлексивных или безопасности исключений.

Параметры:
type - желаемый целевой тип
Возвращает:
обработчик-метод, подходящий для вызова любого обработчика-метода, преобразуемого в заданный тип
Исключения:
IllegalArgumentException - если у результирующего обработчика-метода будет слишком много параметров

varHandleExactInvoker

public static MethodHandle varHandleExactInvoker(VarHandle.AccessMode accessMode, MethodType type)
Производит специальный обработчик-метод, который может использоваться для вызова метода с полиморфной сигнатурой доступа на любом VarHandle, чей связанный тип режима доступа совместим с заданным типом. Результирующий обработчик-метод будет иметь тип, точно равный желаемому типу, за исключением того, что он будет принимать дополнительный ведущий аргумент типа VarHandle.
Параметры:
accessMode - режим доступа VarHandle
type - желаемый целевой тип
Возвращает:
обработчик-метод, подходящий для вызова метода режима доступа любого VarHandle, чей тип режима доступа имеет заданный тип.
С:
9

varHandleInvoker

public static MethodHandle varHandleInvoker(VarHandle.AccessMode accessMode, MethodType type)
Производит специальный обработчик-метод, который может использоваться для вызова метода с полиморфной сигнатурой доступа на любом VarHandle, чей связанный тип режима доступа совместим с заданным типом. Результирующий обработчик-метод будет иметь тип, точно равный желаемому типу, за исключением того, что он будет принимать дополнительный ведущий аргумент типа VarHandle.

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

Этот метод эквивалентен следующему коду (хотя он может быть более эффективным): publicLookup().findVirtual(VarHandle.class, accessMode.name(), type)

Параметры:
accessMode - режим доступа VarHandle
type - желаемый целевой тип
Возвращает:
обработчик-метод, подходящий для вызова метода режима доступа любого VarHandle, чей тип режима доступа преобразуем в заданный тип.
С:
9

explicitCastArguments

public static MethodHandle explicitCastArguments(MethodHandle target, MethodType newType)
Производит обработчик-метод, который адаптирует тип данного обработчика-метода к новому типу посредством попарного преобразования типов аргументов и возвращаемого значения. Исходный тип и новый тип должны иметь одинаковое количество аргументов. Результирующий обработчик-метод гарантированно сообщит о типе, равном желаемому новому типу.

Если исходный и новый типы равны, возвращает target.

Разрешены те же преобразования, что и для MethodHandle.asType, и некоторые дополнительные преобразования также применяются, если эти преобразования не удаются. Даны типы T0, T1, одно из следующих преобразований применяется, если возможно, до или вместо любых преобразований, выполненных asType:

  • Если T0 и T1 — ссылки, а T1 — тип интерфейса, то значение типа T0 передается как T1 без преобразования. (Это обращение к интерфейсам соответствует использованию верификатора байткода.)
  • Если T0 — boolean, а T1 — другой примитивный тип, то boolean преобразуется в значение типа byte, 1 для true, 0 для false. (Это обращение соответствует использованию верификатора байткода.)
  • Если T1 — boolean, а T0 — другой примитивный тип, то T0 преобразуется в byte через преобразование Java (JLS 5.5), и проверяется младший бит результата, как если бы это делалось с помощью (x & 1) != 0.
  • Если T0 и T1 — примитивные типы, отличные от boolean, то применяется преобразование Java (JLS 5.5). (В частности, T0 преобразуется в T1 путем расширения и/или сужения.)
  • Если T0 — ссылка, а T1 — примитивный тип, то в момент выполнения будет применено преобразование распаковки, возможно, за которым следует преобразование Java (JLS 5.5) для примитивного значения, возможно, за которым следует преобразование из byte в boolean путем проверки младшего бита.
  • Если T0 — ссылка, а T1 — примитивный тип, и если ссылка равна null в момент выполнения, то вводится нулевое значение.
Параметры:
target - обработчик-метод, который нужно вызвать после повторной типизации аргументов
newType - ожидаемый тип нового обработчика-метода
Возвращает:
обработчик-метод, который делегирует целевому методу после выполнения всех необходимых преобразований аргументов и организует все необходимые преобразования возвращаемых значений
Исключения:
NullPointerException - если любой из аргументов равен null
WrongMethodTypeException - если преобразование невозможно
См. также:
  • MethodHandle.asType(java.lang.invoke.MethodType)

permuteArguments

public static MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder)
Производит обработчик-метод, который адаптирует порядок вызова данного обработчика-метода к новому типу, переупорядочивая аргументы. Результирующий обработчик-метод гарантированно сообщит о типе, равном желаемому новому типу.

Заданный массив управляет переупорядочиванием. Вызовите #I количество входных параметров (значение newType.parameterCount()), и вызовите #O количество выходных параметров (значение target.type().parameterCount()). Тогда длина массива переупорядочения должна быть #O, и каждый элемент должен быть неотрицательным числом, меньшим #I. Для каждого N меньше #O, N-й выходной аргумент будет взят из I-го входного аргумента, где I является reorder[N].

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

Массив переупорядочения не обязательно должен задавать фактическое переупорядочение. Входной аргумент будет дублироваться, если его индекс встречается более одного раза в массиве, и входной аргумент будет отброшен, если его индекс не встречается в массиве. Как и в случае с dropArguments, входные аргументы, которые не упомянуты в массиве переупорядочения, могут быть любого типа, определяемого только newType.


import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
...
MethodType intfn1 = methodType(int.class, int.class);
MethodType intfn2 = methodType(int.class, int.class, int.class);
MethodHandle sub = ... (int x, int y) -> (x-y) ...;
assert(sub.type().equals(intfn2));
MethodHandle sub1 = permuteArguments(sub, intfn2, 0, 1);
MethodHandle rsub = permuteArguments(sub, intfn2, 1, 0);
assert((int)rsub.invokeExact(1, 100) == 99);
MethodHandle add = ... (int x, int y) -> (x+y) ...;
assert(add.type().equals(intfn2));
MethodHandle twice = permuteArguments(add, intfn1, 0, 0);
assert(twice.type().equals(intfn1));
assert((int)twice.invokeExact(21) == 42);
 

Примечание: Полученный адаптер никогда не является обработчиком-методом с переменным числом аргументов, даже если исходный целевой обработчик-метод был таковым.

Параметры:
target - обработчик-метод, который нужно вызвать после переупорядочения аргументов
newType - ожидаемый тип нового обработчика-метода
reorder - массив индексов, который управляет переупорядочением
Возвращает:
обработчик-метод, который делегирует целевому методу после отбрасывания неиспользуемых аргументов и перемещения и/или дублирования других аргументов
Исключения:
NullPointerException - если любой аргумент равен null
IllegalArgumentException - если длина массива индексов не равна арности целевого метода, или если любой элемент массива индексов не является допустимым индексом для параметра newType, или если два соответствующих типа параметров в target.type() и newType не идентичны,

constant

public static MethodHandle constant(Class<?> type, Object value)
Создаёт обработчик метода требуемого типа возврата, который каждый раз при вызове возвращает заданное константное значение.

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

Возвращаемый обработчик метода эквивалентен identity(type).bindTo(value).

Parameters:
type - тип возврата желаемого обработчика метода
value - значение, которое должно быть возвращено
Returns:
обработчик метода заданного типа возврата и без аргументов, который всегда возвращает заданное значение
Throws:
NullPointerException - если аргумент type имеет значение null
ClassCastException - если значение нельзя преобразовать в требуемый тип возврата
IllegalArgumentException - если указанный тип является void.class

identity

public static MethodHandle identity(Class<?> type)
Создаёт обработчик метода, который возвращает единственный аргумент при вызове.
Parameters:
type - тип единственного параметра и значения возврата желаемого обработчика метода
Returns:
унарный обработчик метода, принимающий и возвращающий указанный тип
Throws:
NullPointerException - если аргумент имеет значение null
IllegalArgumentException - если указанный тип является void.class

zero

public static MethodHandle zero(Class<?> type)
Создаёт константный обработчик метода требуемого типа возврата, который каждый раз при вызове возвращает значение по умолчанию для этого типа. Результирующий константный обработчик метода не имеет побочных эффектов.

Возвращаемый обработчик метода эквивалентен empty(methodType(type)). Он также эквивалентен explicitCastArguments(constant(Object.class, null), methodType(type)), так как explicitCastArguments преобразует null в значения по умолчанию.

Parameters:
type - ожидаемый тип возврата желаемого обработчика метода
Returns:
константный обработчик метода, который не принимает аргументов и возвращает значение по умолчанию для заданного типа (или void, если тип — void)
Throws:
NullPointerException - если аргумент имеет значение null
Since:
9
See Also:
  • constant(java.lang.Class<?>, java.lang.Object)
  • empty(java.lang.invoke.MethodType)
  • explicitCastArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)

empty

public static MethodHandle empty(MethodType type)
Создаёт обработчик метода запрошенного типа, который игнорирует любые аргументы, ничего не делает и возвращает подходящее значение по умолчанию в зависимости от типа возврата. То есть, возвращает нулевое примитивное значение, null, или void.

Возвращаемый обработчик метода эквивалентен dropArguments(zero(type.returnType()), 0, type.parameterList()).

API Note:
Исходя из предиката и целевого объекта, можно создать полезную конструкцию «если-то» в виде guardWithTest(pred, target, empty(target.type()).
Parameters:
type - тип желаемого обработчика метода
Returns:
константный обработчик метода заданного типа, который возвращает значение по умолчанию для данного типа возврата
Throws:
NullPointerException - если аргумент имеет значение null
Since:
9
See Also:
  • zero(java.lang.Class<?>)
  • constant(java.lang.Class<?>, java.lang.Object)

insertArguments

public static MethodHandle insertArguments(MethodHandle target, int pos, Object... values)
Предоставляет целевому обработчику метода один или несколько связанных аргументов предварительно вызова обработчика метода. Формальные параметры целевого метода, соответствующие связанным аргументам, называются связанными параметрами. Возвращает новый обработчик метода, который сохраняет связанные аргументы. При вызове он получает аргументы для любых несвязанных параметров, связывает сохранённые аргументы с соответствующими параметрами и вызывает исходный целевой объект.

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

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

Аргумент pos выбирает, какие параметры нужно связать. Он может принимать значения от нуля до N-L (включительно), где N — арность целевого обработчика метода, а L — длина массива значений.

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

Parameters:
target - обработчик метода, который нужно вызвать после вставки аргумента
pos - место вставки аргумента (ноль для первого)
values - последовательность аргументов для вставки
Returns:
обработчик метода, который вставляет дополнительный аргумент перед вызовом исходного обработчика метода
Throws:
NullPointerException - если целевой объект или массив values имеет значение null
IllegalArgumentException - если (@code pos) меньше 0 или больше N - L, где N — арность целевого обработчика метода, а L — длина массива значений.
ClassCastException - если аргумент не соответствует типу соответствующего связанного параметра.
See Also:
  • MethodHandle.bindTo(java.lang.Object)

dropArguments

public static MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes)
Создаёт обработчик метода, который отбросит некоторые фиктивные аргументы перед вызовом другого указанного целевого обработчика метода. Тип нового обработчика метода будет таким же, как тип целевого, за исключением того, что он также будет включать типы фиктивных аргументов в определённой позиции.

Аргумент pos может принимать значения от нуля до N, где N — арность целевого объекта. Если pos равно нулю, фиктивные аргументы будут стоять перед реальными аргументами целевого объекта; если pos равно N, они будут стоять после.

Пример:


import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
...
MethodHandle cat = lookup().findVirtual(String.class,
  "concat", methodType(String.class, String.class));
assertEquals("xy", (String) cat.invokeExact("x", "y"));
MethodType bigType = cat.type().insertParameterTypes(0, int.class, String.class);
MethodHandle d0 = dropArguments(cat, 0, bigType.parameterList().subList(0,2));
assertEquals(bigType, d0.type());
assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z"));
 

Этот метод также эквивалентен следующему коду:

 dropArguments(target, pos, valueTypes.toArray(new Class[0]))
 
Parameters:
target - обработчик метода, который нужно вызвать после отбрасывания аргументов
pos - позиция первого аргумента для отбрасывания (ноль для левого)
valueTypes - тип(ы) аргумента(ов) для отбрасывания
Returns:
обработчик метода, который отбрасывает аргументы заданных типов перед вызовом исходного обработчика метода
Throws:
NullPointerException - если целевой объект имеет значение null, или если список valueTypes или любой из его элементов имеет значение null
IllegalArgumentException - если любой элемент valueTypes имеет значение void.class, или если pos отрицательное или больше арности целевого объекта, или если тип нового обработчика метода будет иметь слишком много параметров

dropArguments

public static MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes)
Создаёт обработчик метода, который отбросит некоторые фиктивные аргументы перед вызовом другого указанного целевого обработчика метода. Тип нового обработчика метода будет таким же, как тип целевого, за исключением того, что он также будет включать типы фиктивных аргументов в определённой позиции.

Аргумент pos может принимать значения от нуля до N, где N — арность целевого объекта. Если pos равно нулю, фиктивные аргументы будут стоять перед реальными аргументами целевого объекта; если pos равно N, они будут стоять после.

API Note:

import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
...
MethodHandle cat = lookup().findVirtual(String.class,
  "concat", methodType(String.class, String.class));
assertEquals("xy", (String) cat.invokeExact("x", "y"));
MethodHandle d0 = dropArguments(cat, 0, String.class);
assertEquals("yz", (String) d0.invokeExact("x", "y", "z"));
MethodHandle d1 = dropArguments(cat, 1, String.class);
assertEquals("xz", (String) d1.invokeExact("x", "y", "z"));
MethodHandle d2 = dropArguments(cat, 2, String.class);
assertEquals("xy", (String) d2.invokeExact("x", "y", "z"));
MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class);
assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
 

Этот метод также эквивалентен следующему коду:

 dropArguments(target, pos, Arrays.asList(valueTypes))
 
Parameters:
target - обработчик метода, который нужно вызвать после отбрасывания аргументов
pos - позиция первого аргумента для отбрасывания (ноль для левого)
valueTypes - тип(ы) аргумента(ов) для отбрасывания
Returns:
обработчик метода, который отбрасывает аргументы заданных типов перед вызовом исходного обработчика метода
Throws:
NullPointerException - если целевой объект имеет значение null, или если массив valueTypes или любой из его элементов имеет значение null
IllegalArgumentException - если любой элемент valueTypes имеет значение void.class, или если pos отрицательное или больше арности целевого объекта, или если тип нового обработчика метода будет иметь слишком много параметров

dropArgumentsToMatch

public static MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List<Class<?>> newTypes, int pos)
Адаптирует целевой метод-обработчик для соответствия заданному списку типов параметров. Если необходимо, добавляет фиктивные аргументы. Некоторые ведущие параметры могут быть пропущены до начала сопоставления. Остальные типы в списке типов параметров target должны быть подмножеством списка типов newTypes в начальной позиции pos. Результирующий обработчик будет иметь список типов параметров целевого обработчика, с любыми несоответствующими типами параметров (до или после совпадающего подсписка), вставленными в соответствующие позиции исходных параметров целевого, как если бы он был получен с помощью dropArguments(MethodHandle, int, Class[]).

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

В более формальных терминах, предположим эти два списка типов:

  • Целевой обработчик имеет список типов параметров S..., M..., с таким количеством типов в S как указано в skip. Типы M - это те, которые должны совпасть с частью заданного списка типов, newTypes.
  • Список newTypes содержит типы P..., M..., A..., с таким количеством типов в P как указано в pos. Типы M - это именно те типы, которые типы M в списке типов параметров целевого обработчика должны совпасть. Типы в A - это дополнительные типы, найденные после совпадающего подсписка.
При данных предположениях, результат вызова dropArgumentsToMatch будет иметь список типов параметров S..., P..., M..., A..., с типами P и A вставленными как если бы они были получены с помощью dropArguments(MethodHandle, int, Class[]).
API Note:
Два метода-обработчика, списки аргументов которых "эффективно идентичны" (т.е., идентичны в общем префиксе), могут быть взаимно преобразованы в общий тип двумя вызовами dropArgumentsToMatch, как показано ниже:

import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
...
...
MethodHandle h0 = constant(boolean.class, true);
MethodHandle h1 = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class));
MethodType bigType = h1.type().insertParameterTypes(1, String.class, int.class);
MethodHandle h2 = dropArguments(h1, 0, bigType.parameterList());
if (h1.type().parameterCount() < h2.type().parameterCount())
    h1 = dropArgumentsToMatch(h1, 0, h2.type().parameterList(), 0);  // lengthen h1
else
    h2 = dropArgumentsToMatch(h2, 0, h1.type().parameterList(), 0);    // lengthen h2
MethodHandle h3 = guardWithTest(h0, h1, h2);
assertEquals("xy", h3.invoke("x", "y", 1, "a", "b", "c"));
 
Параметры:
target - метод-обработчик для адаптации
skip - количество параметров целевого объекта, которые следует игнорировать (они останутся неизменными)
newTypes - список типов для сопоставления списка типов параметров target
pos - позиция в newTypes, где должны находиться не пропущенные параметры целевого объекта
Возвращает:
возможно адаптированный метод-обработчик
Исключения:
NullPointerException - если любой из аргументов равен null
IllegalArgumentException - если любой элемент newTypes равен void.class, или если skip отрицательно или больше арности целевого объекта, или если pos отрицательно или больше размера списка newTypes, или если newTypes не содержит не пропущенные типы параметров целевого объекта в позиции pos.
С:
9

dropReturn

public static MethodHandle dropReturn(MethodHandle target)
Игнорирует возвращаемое значение целевого обработчика (если таковое имеется). Возвращаемый метод-обработчик будет иметь тип void возвращаемого значения.
Параметры:
target - метод-обработчик для адаптации
Возвращает:
возможно адаптированный метод-обработчик
Исключения:
NullPointerException - если target равен null
С:
16

filterArguments

public static MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters)
Адаптирует целевой метод-обработчик, предварительно обрабатывая один или несколько его аргументов, каждый со своей унарной функцией фильтрации, и затем вызывая целевой объект с каждым предварительно обработанным аргументом, заменённым результатом соответствующей функции фильтрации.

Предварительная обработка выполняется одним или несколькими методами-обработчиками, указанными в элементах массива filters. Первый элемент массива фильтров соответствует аргументу pos целевого объекта, и так далее последовательно. Функции фильтра вызываются слева направо.

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

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

Ошибка возникает, если есть элементы массива filters (null или не null), которые не соответствуют позициям аргументов в целевом объекте.

Пример:


import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
...
MethodHandle cat = lookup().findVirtual(String.class,
  "concat", methodType(String.class, String.class));
MethodHandle upcase = lookup().findVirtual(String.class,
  "toUpperCase", methodType(String.class));
assertEquals("xy", (String) cat.invokeExact("x", "y"));
MethodHandle f0 = filterArguments(cat, 0, upcase);
assertEquals("Xy", (String) f0.invokeExact("x", "y")); // Xy
MethodHandle f1 = filterArguments(cat, 1, upcase);
assertEquals("xY", (String) f1.invokeExact("x", "y")); // xY
MethodHandle f2 = filterArguments(cat, 0, upcase, upcase);
assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
 

Вот псевдокод для результирующего адаптера. В коде T обозначает тип возвращаемого значения как target, так и результирующего адаптера. P/p и B/b представляют типы и значения параметров и аргументов, которые предшествуют и следуют за позицией фильтра pos, соответственно. A[i]/a[i] обозначают типы и значения отфильтрованных параметров и аргументов; они также представляют типы возвращаемых значений методов-обработчиков filter[i]. Последние принимают аргументы v[i] типа V[i], которые также появляются в сигнатуре результирующего адаптера.


 T target(P... p, A[i]... a[i], B... b);
 A[i] filter[i](V[i]);
 T adapter(P... p, V[i]... v[i], B... b) {
   return target(p..., filter[i](v[i])..., b...);
 }
 

Примечание: Полученный адаптер никогда не является методом-обработчиком с переменным числом аргументов, даже если исходный целевой метод-обработчик был им.

Параметры:
target - метод-обработчик для вызова после фильтрации аргументов
pos - позиция первого аргумента для фильтрации
filters - методы-обработчики для первоначального вызова на отфильтрованных аргументах
Возвращает:
метод-обработчик, который включает указанную логику фильтрации аргументов
Исключения:
NullPointerException - если целевой объект равен null или если массив filters равен null
IllegalArgumentException - если какой-либо ненулевой элемент filters не соответствует соответствующему типу аргумента целевого объекта, как описано выше, или если pos+filters.length больше target.type().parameterCount(), или если тип результирующего метода-обработчика будет иметь слишком много параметров

collectArguments

public static MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle filter)
Адаптирует целевой обработчик метода путём предварительной обработки подпоследовательности его аргументов с помощью фильтра (другого обработчика метода). Предварительно обработанные аргументы заменяются результатом (если таковой имеется) функции фильтра. Затем целевой метод вызывается с изменённым (обычно укороченным) списком аргументов.

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

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

Во всех случаях pos должно быть больше или равно нулю, а pos также должно быть меньше или равно арности целевого метода.

Пример:


import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
...
MethodHandle deepToString = publicLookup()
  .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));

MethodHandle ts1 = deepToString.asCollector(String[].class, 1);
assertEquals("[strange]", (String) ts1.invokeExact("strange"));

MethodHandle ts2 = deepToString.asCollector(String[].class, 2);
assertEquals("[up, down]", (String) ts2.invokeExact("up", "down"));

MethodHandle ts3 = deepToString.asCollector(String[].class, 3);
MethodHandle ts3_ts2 = collectArguments(ts3, 1, ts2);
assertEquals("[top, [up, down], strange]",
             (String) ts3_ts2.invokeExact("top", "up", "down", "strange"));

MethodHandle ts3_ts2_ts1 = collectArguments(ts3_ts2, 3, ts1);
assertEquals("[top, [up, down], [strange]]",
             (String) ts3_ts2_ts1.invokeExact("top", "up", "down", "strange"));

MethodHandle ts3_ts2_ts3 = collectArguments(ts3_ts2, 1, ts3);
assertEquals("[top, [[up, down, strange], charm], bottom]",
             (String) ts3_ts2_ts3.invokeExact("top", "up", "down", "strange", "charm", "bottom"));
 

Вот псевдокод для результирующего адаптера. В коде T представляет тип возвращаемого значения target и результирующего адаптера. V/v обозначают тип возвращаемого значения и значение filter, которые также присутствуют в сигнатуре и аргументах target, соответственно, если V не void. A/a и C/c обозначают типы параметров и значения, предшествующие и следующие за позицией сбора pos в сигнатуре target. Они также появляются в сигнатуре результирующего адаптера и аргументах, где они окружают B/b, которые представляют типы параметров и аргументы filter (если таковые имеются).


 T target(A...,V,C...);
 V filter(B...);
 T adapter(A... a,B... b,C... c) {
   V v = filter(b...);
   return target(a...,v,c...);
 }
 // and if the filter has no arguments:
 T target2(A...,V,C...);
 V filter2();
 T adapter2(A... a,C... c) {
   V v = filter2();
   return target2(a...,v,c...);
 }
 // and if the filter has a void return:
 T target3(A...,C...);
 void filter3(B...);
 T adapter3(A... a,B... b,C... c) {
   filter3(b...);
   return target3(a...,c...);
 }
 

Адаптер сбора collectArguments(mh, 0, coll) эквивалентен тому, который сначала "складывает" затронутые аргументы, а затем удаляет их, в отдельных шагах следующим образом:


 mh = MethodHandles.dropArguments(mh, 1, coll.type().parameterList()); //step 2
 mh = MethodHandles.foldArguments(mh, coll); //step 1
 
Если целевой обработчик метода потребляет никаких аргументов, кроме результата (если таковой имеется) фильтра coll, то collectArguments(mh, 0, coll) эквивалентно filterReturnValue(coll, mh). Если обработчик метода фильтра coll потребляет один аргумент и производит не-пустое значение, то collectArguments(mh, N, coll) эквивалентно filterArguments(mh, N, coll). Другие эквивалентности возможны, но потребуют перестановки аргументов.

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

Parameters:
target - обработчик метода, который будет вызван после фильтрации подпоследовательности аргументов
pos - позиция первого аргумента адаптера, передаваемого фильтру, и/или аргумента целевого метода, который получает результат фильтра
filter - обработчик метода, вызываемый на подпоследовательности аргументов
Returns:
обработчик метода, который включает в себя указанную логику фильтрации подпоследовательности аргументов
Throws:
NullPointerException - если какой-либо из аргументов равен null
IllegalArgumentException - если тип возвращаемого значения filter не void и не совпадает с типом аргумента pos целевого метода, или если pos не находится в пределах от 0 до арности целевого метода включительно, или если тип результирующего обработчика метода имел бы слишком много параметров
See Also:
  • foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle)
  • filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...)
  • filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle)

filterReturnValue

public static MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter)
Адаптирует целевой обработчик метода путём последующей обработки возвращаемого значения (если таковое имеется) с помощью фильтра (другого обработчика метода). Результат фильтра возвращается из адаптера.

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

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

Пример:


import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
...
MethodHandle cat = lookup().findVirtual(String.class,
  "concat", methodType(String.class, String.class));
MethodHandle length = lookup().findVirtual(String.class,
  "length", methodType(int.class));
System.out.println((String) cat.invokeExact("x", "y")); // xy
MethodHandle f0 = filterReturnValue(cat, length);
System.out.println((int) f0.invokeExact("x", "y")); // 2
 

Вот псевдокод для результирующего адаптера. В коде T/t представляют тип возвращаемого значения и значение target; V, тип возвращаемого значения filter; а A/a, типы и значения параметров и аргументов target, а также результирующего адаптера.


 T target(A...);
 V filter(T);
 V adapter(A... a) {
   T t = target(a...);
   return filter(t);
 }
 // and if the target has a void return:
 void target2(A...);
 V filter2();
 V adapter2(A... a) {
   target2(a...);
   return filter2();
 }
 // and if the filter has a void return:
 T target3(A...);
 void filter3(V);
 void adapter3(A... a) {
   T t = target3(a...);
   filter3(t);
 }
 

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

Parameters:
target - обработчик метода, который будет вызван до фильтрации возвращаемого значения
filter - обработчик метода, вызываемый на возвращаемом значении
Returns:
обработчик метода, который включает в себя указанную логику фильтрации возвращаемого значения
Throws:
NullPointerException - если какой-либо из аргументов равен null
IllegalArgumentException - если список аргументов filter не соответствует типу возвращаемого значения целевого метода, как описано выше

foldArguments

public static MethodHandle foldArguments(MethodHandle target, MethodHandle combiner)
Адаптирует целевой обработчик метода, предварительно обрабатывая некоторые его аргументы, а затем вызывая целевой метод с результатом предварительной обработки, вставленным в исходную последовательность аргументов.

Предварительная обработка выполняется combiner, вторым обработчиком метода. Из аргументов, переданных адаптеру, первые N аргументов копируются в комбинирующее устройство, которое затем вызывается. (Здесь N определяется как количество параметров комбинирующего устройства.) После этого управление передаётся целевому методу, а любой результат от комбинирующего устройства вставляется перед исходными N входными аргументами.

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

Если комбинирующее устройство возвращает void, результат не будет вставлен, и первые N типы параметров целевого метода должны точно соответствовать параметрам комбинирующего устройства.

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

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

Пример:


import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
...
MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
  "println", methodType(void.class, String.class))
    .bindTo(System.out);
MethodHandle cat = lookup().findVirtual(String.class,
  "concat", methodType(String.class, String.class));
assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
MethodHandle catTrace = foldArguments(cat, trace);
// also prints "boo":
assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
 

Вот псевдокод для результирующего адаптера. В коде T представляет тип возвращаемого значения target и результирующего адаптера. V/v представляют тип и значение параметра и аргумента target перед позицией складывания; V также является типом возвращаемого значения combiner. A/a обозначают типы и значения параметров и аргументов N на позиции складывания. B/b представляют типы и значения параметров и аргументов target, которые следуют за сложенными параметрами и аргументами.


 // there are N arguments in A...
 T target(V, A[N]..., B...);
 V combiner(A...);
 T adapter(A... a, B... b) {
   V v = combiner(a...);
   return target(v, a..., b...);
 }
 // and if the combiner has a void return:
 T target2(A[N]..., B...);
 void combiner2(A...);
 T adapter2(A... a, B... b) {
   combiner2(a...);
   return target2(a..., b...);
 }
 

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

Parameters:
target - обработчик метода, который будет вызван после комбинирования аргументов
combiner - обработчик метода, вызываемый первоначально на входных аргументах
Returns:
обработчик метода, который включает в себя указанную логику складывания аргументов
Throws:
NullPointerException - если какой-либо из аргументов равен null
IllegalArgumentException - если тип возвращаемого значения combiner не void и не совпадает с первым типом аргумента целевого метода, или если начальные N типы аргументов целевого метода (пропуская один, соответствующий типу возвращаемого значения combiner ) не идентичны типам аргументов combiner

foldArguments

public static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner)
Адаптирует целевой обработчик методов, предварительно обработав некоторые его аргументы, начиная с заданной позиции, а затем вызвав целевой обработчик с результатом предварительной обработки, вставленным в исходную последовательность аргументов непосредственно перед сложенными аргументами.

Этот метод тесно связан с foldArguments(MethodHandle, MethodHandle), но позволяет управлять позицией в списке параметров, в которой происходит условное складывание. Аргумент, контролирующий это, pos, представляет собой индекс, начинающийся с нуля. Указанный метод foldArguments(MethodHandle, MethodHandle) предполагает позицию 0.

API Note:
Пример:

    import static java.lang.invoke.MethodHandles.*;
    import static java.lang.invoke.MethodType.*;
    ...
    MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
    "println", methodType(void.class, String.class))
    .bindTo(System.out);
    MethodHandle cat = lookup().findVirtual(String.class,
    "concat", methodType(String.class, String.class));
    assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
    MethodHandle catTrace = foldArguments(cat, 1, trace);
    // also prints "jum":
    assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
 

Вот псевдокод для результирующего адаптера. В коде T представляет собой тип результата target и результирующего адаптера. V/v представляют тип и значение параметра и аргумента target, предшествующего позиции складывания; V также является типом результата combiner. A/a обозначают типы и значения параметров и аргументов N на позиции складывания. Z/z и B/b представляют типы и значения параметров и аргументов target, которые предшествуют и следуют за сложенными параметрами и аргументами, начиная с pos, соответственно.


 // there are N arguments in A...
 T target(Z..., V, A[N]..., B...);
 V combiner(A...);
 T adapter(Z... z, A... a, B... b) {
   V v = combiner(a...);
   return target(z..., v, a..., b...);
 }
 // and if the combiner has a void return:
 T target2(Z..., A[N]..., B...);
 void combiner2(A...);
 T adapter2(Z... z, A... a, B... b) {
   combiner2(a...);
   return target2(z..., a..., b...);
 }
 

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

Параметры:
target - обработчик метода для вызова после объединения аргументов
pos - позиция, с которой начинать складывание и в которую вставлять результат складывания; если это 0, эффект такой же, как и для foldArguments(MethodHandle, MethodHandle).
combiner - обработчик метода для вызова вначале на входных аргументах
Возвращает:
обработчик метода, который включает указанную логику условного складывания аргументов
Исключения:
NullPointerException - если любой из аргументов равен null
IllegalArgumentException - если выполняется любое из двух условий: (1) тип возвращаемого значения combiner не является void и не совпадает с типом аргумента на позиции pos целевой подписи; (2) типы аргументов N на позиции pos целевой подписи (пропуская один, соответствующий типу возвращаемого значения combiner) не идентичны типам аргументов combiner.
С:
9
См. также:
  • foldArguments(MethodHandle, MethodHandle)

guardWithTest

public static MethodHandle guardWithTest(MethodHandle test, MethodHandle target, MethodHandle fallback)
Создает обработчик метода, который адаптирует целевой обработчик метода, защищая его тестом, булевым обработчиком метода. Если проверка не пройдена, вместо этого вызывается обработчик по умолчанию. Все три обработчика методов должны иметь одинаковые соответствующие типы аргументов и возвращаемых значений, за исключением того, что тип возвращаемого значения теста должен быть boolean, и тест может иметь меньше аргументов, чем два других обработчика методов.

Вот псевдокод для результирующего адаптера. В коде T представляет собой унифицированный тип результата трёх задействованных обработчиков; A/a, типы и значения параметров и аргументов target потребляемых test; и B/b, те типы и значения параметров и аргументов target не потребляемых test.


 boolean test(A...);
 T target(A...,B...);
 T fallback(A...,B...);
 T adapter(A... a,B... b) {
   if (test(a...))
     return target(a..., b...);
   else
     return fallback(a..., b...);
 }
 
Обратите внимание, что аргументы теста (a... в псевдокоде) не могут быть изменены во время выполнения теста и, следовательно, передаются без изменений от вызывающего обработчика к целевому обработчику или обработчику по умолчанию в зависимости от обстоятельств.
Параметры:
test - обработчик метода, используемый для теста, должен возвращать boolean
target - обработчик метода, вызываемый, если тест пройден
fallback - обработчик метода, вызываемый, если тест не пройден
Возвращает:
обработчик метода, включающий указанную логику if/then/else
Исключения:
NullPointerException - если какой-либо аргумент равен null
IllegalArgumentException - если test не возвращает boolean или если типы всех трёх обработчиков методов не совпадают (с типом возвращаемого значения test, изменённым для соответствия целевому).

catchException

public static MethodHandle catchException(MethodHandle target, Class<? extends Throwable> exType, MethodHandle handler)
Создаёт обработчик метода, который адаптирует целевой обработчик метода, выполняя его внутри обработчика исключений. Если целевой обработчик возвращает значение нормально, адаптер возвращает это значение. Если возникает исключение, соответствующее указанному типу, вместо этого вызывается обработчик по умолчанию на исключении и исходных аргументах.

Целевой обработчик и обработчик должны иметь одинаковые соответствующие типы аргументов и возвращаемых значений, за исключением того, что обработчик может опустить хвостовые аргументы (аналогично предикату в guardWithTest). Кроме того, обработчик должен иметь дополнительный ведущий параметр типа exType или его супертип.

Вот псевдокод для результирующего адаптера. В коде T представляет тип возвращаемого значения target и handler, а также результирующего адаптера; A/a, типы и значения аргументов результата, потребляемых handler; и B/b, типы и значения аргументов результата, игнорируемых handler.


 T target(A..., B...);
 T handler(ExType, A...);
 T adapter(A... a, B... b) {
   try {
     return target(a..., b...);
   } catch (ExType ex) {
     return handler(ex, a...);
   }
 }
 
Обратите внимание, что сохранённые аргументы (a... в псевдокоде) не могут быть изменены во время выполнения целевого обработчика и, следовательно, передаются без изменений от вызывающего обработчика к обработчику, если он вызывается.

Целевой обработчик и обработчик должны возвращать один и тот же тип, даже если обработчик всегда выбрасывает исключение. (Это может произойти, например, потому, что обработчик моделирует finally условие). Чтобы создать такой обработчик, выбрасывающий исключение, составьте логику создания обработчика с throwException, чтобы создать обработчик метода с правильным типом возвращаемого значения.

Параметры:
target - обработчик метода для вызова
exType - тип исключения, который обработчик будет перехватывать
handler - обработчик метода для вызова, если возникает соответствующее исключение
Возвращает:
обработчик метода, включающий указанную логику try/catch
Исключения:
NullPointerException - если какой-либо аргумент равен null
IllegalArgumentException - если handler не принимает данный тип исключения или если типы обработчиков методов не совпадают в своих типах возвращаемых значений и соответствующих параметрах
См. также:
  • tryFinally(MethodHandle, MethodHandle)

throwException

public static MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType)
Создаёт обработчик метода, который будет выбрасывать исключения указанного exType. Обработчик метода будет принимать один аргумент типа exType, и немедленно сгенерирует его как исключение. Тип возвращаемого значения метода номинально будет returnType. Тип возвращаемого значения может быть любым удобным: он не имеет значения для поведения обработчика метода, так как он никогда не вернётся нормально.
Параметры:
returnType - тип возвращаемого значения желаемого обработчика метода
exType - тип параметра желаемого обработчика метода
Возвращает:
обработчик метода, который может выбрасывать заданные исключения
Исключения:
NullPointerException - если любой из аргументов равен null
END_OF_DOCUMENT_MARKER

цикл

public static MethodHandle loop(MethodHandle[]... clauses)
Создаёт обработчик метода, представляющий цикл с несколькими переменными цикла, которые обновляются и проверяются на каждой итерации. По завершении цикла из-за одного из предикатов, выполняется соответствующий финализатор, который возвращает результат цикла, который является возвращаемым значением полученного обработчика.

Интуитивно, каждый цикл состоит из одной или нескольких "клауз", каждая из которых определяет локальную переменную итерации и/или условие выхода из цикла. Каждая итерация цикла выполняет каждую клаузу в порядке. Клауза может опционально обновить свою переменную итерации; она также может опционально выполнить проверку и условный выход из цикла. Для выражения этой логики с помощью обработчиков методов каждая клауза будет указывать до четырёх независимых действий:

  • инициализация: Перед выполнением цикла инициализация переменной итерации v типа V.
  • шаг: При выполнении клаузы, шаг обновления переменной итерации v.
  • предикат: При выполнении клаузы, выполнение предиката для проверки выхода из цикла.
  • финализация: Если клауза вызывает выход из цикла, выполнение финализатора для вычисления возвращаемого значения цикла.
Полная последовательность всех типов переменных итерации в порядке клауз будет записана как (V...). Значения сами по себе будут (v...). Когда мы говорим о "списках параметров", мы обычно будем ссылаться на типы, но в некоторых контекстах (описывая выполнение) списки будут фактическими значениями.

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

Опциональные параметры повсюду: Функция каждой клаузы может, но не обязана принимать параметр для каждой переменной итерации v. В качестве исключения, функции инициализации не могут принимать какие-либо v параметры, потому что эти значения ещё не вычислены на момент выполнения функций инициализации. Любая функция клаузы может пренебречь приёмом любого последующего подмножества параметров, которые она имеет право принять. На самом деле, любая функция клаузы может вообще не принимать никаких аргументов.

Параметры цикла: Функция клаузы может принять все значения переменных итерации, на которые она имеет право, в этом случае она также может принять дополнительные параметры. Такие дополнительные значения называются параметрами цикла, с их типами и значениями, записанными как (A...) и (a...). Они становятся параметрами результирующего обработчика цикла, которые должны быть предоставлены при каждом выполнении цикла. (Так как функции инициализации не принимают переменные итерации v, любой параметр функции инициализации автоматически является параметром цикла a.) Как и в случае с переменными итерации, функции клауз могут, но не обязаны принимать параметры цикла. Эти параметры цикла действуют как инвариантные значения цикла, видимые во всём цикле.

Параметры, видимые повсюду: Каждая функция клаузы, не являющаяся функцией инициализации, может наблюдать за полным состоянием цикла, потому что ей может быть передан полный список (v... a...) текущих значений переменных итерации и входящих параметров цикла. Функции инициализации могут наблюдать за начальным состоянием до цикла в виде (a...). Большинству функций клауз не потребуется вся эта информация, но они будут формально связаны с ней так, как будто с помощью dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>). Более конкретно, мы будем использовать обозначение (V*) для выражения произвольного префикса полной последовательности (V...) (и аналогично для (v*), (A*), (a*)). В этом обозначении общий вид списка параметров функции инициализации — (A*), а общий вид списка параметров функции, не являющейся функцией инициализации, — (V*) или (V... A*).

Проверка структуры клаузы: Для заданного набора клауз выполняется ряд проверок и корректировок для соединения всех частей цикла. Они подробно описаны в шагах ниже. В этих шагах каждое вхождение слова "должен" соответствует месту, где IllegalArgumentException будет выброшено, если требуемое ограничение не соблюдается входными данными для комбинатора цикла.

Эффективно идентичные последовательности: Список параметров A определяется как эффективно идентичный другому списку параметров B , если A и B идентичны, или если A короче и идентичен правильному префиксу B. Говоря об неупорядоченном наборе списков параметров, мы говорим, что набор "эффективно идентичен" в целом, если набор содержит самый длинный список, и все члены набора эффективно идентичны этому самому длинному списку. Например, любой набор последовательностей типов вида (V*) эффективно идентичен, и то же самое верно, если добавлены дополнительные последовательности вида (V... A*).

Шаг 0: Определение структуры клаузы.

  1. Массив клауз (типа MethodHandle[][]) должен быть не-null и содержать по крайней мере один элемент.
  2. Массив клауз не может содержать null или подмассивы длиной более четырёх элементов.
  3. Клаузы, короче четырёх элементов, обрабатываются так, как будто они были дополнены элементами null до длины четыре. Дополнение происходит путём добавления элементов в массив.
  4. Клаузы со всеми null игнорируются.
  5. Каждая клауза обрабатывается как четвёрка функций, называемых "инициализация", "шаг", "предикат" и "финализация".

Шаг 1А: Определение типов переменных итерации (V...).

  1. Тип переменной итерации для каждой клаузы определяется с помощью типов возврата функций инициализации и шага клаузы.
  2. Если обе функции опушены, переменная итерации для соответствующей клаузы отсутствует (void используется как тип, чтобы указать это). Если одна из них опущена, тип возврата другой определяет тип переменной итерации клаузы. Если обе указаны, общий тип возврата (они должны быть идентичны) определяет тип переменной итерации клаузы.
  3. Сформировать список типов возврата (в порядке клауз), опуская все вхождения void.
  4. Этот список типов называется "типами переменных итерации" ((V...)).

Шаг 1B: Определение параметров цикла (A...).

  • Рассмотреть и собрать списки параметров функций инициализации (которые имеют вид (A*)).
  • Рассмотреть и собрать суффиксы списков параметров шага, предиката и финализации после удаления типов переменных итерации. (Они должны иметь вид (V... A*); собирать только части (A*)).
  • Не собирать суффиксы из списков параметров шага, предиката и финализации, которые не начинаются со всех типов переменных итерации. (Эти типы будут проверены на шаге 2 вместе со всеми типами функций клаузы).
  • Опущенные функции клаузы игнорируются. (Эквивалентно, считается, что у них пустые списки параметров.)
  • Все собранные списки параметров должны быть эффективно идентичными.
  • Самый длинный список параметров (который, неизбежно, единственный) называется "внешним списком параметров" ((A...)).
  • Если такого списка параметров нет, внешний список параметров принимается как пустая последовательность.
  • Объединённый список, состоящий из типов переменных итерации, за которыми следуют типы внешних параметров, называется "внутренним списком параметров".

Шаг 1C: Определение типа возврата цикла.

  1. Рассмотреть типы возврата функций финализации, игнорируя опущенные функции финализации.
  2. Если функций финализации нет, тип возврата цикла — void.
  3. В противном случае, общий тип возврата R функций финализации (их типы возврата должны быть идентичны) определяет тип возврата цикла.

Шаг 1D: Проверка других типов.

  1. Должна быть по крайней мере одна не пропущенная функция предиката.
  2. Каждая не пропущенная функция предиката должна иметь тип возврата boolean.

Шаг 2: Определение списков параметров.

  1. Список параметров для результирующего обработчика цикла будет внешним списком параметров (A...).
  2. Список параметров для функций инициализации будет скорректирован до внешнего списка параметров. (Обратите внимание, что их списки параметров уже эффективно идентичны этому списку.)
  3. Список параметров для каждой не пропущенной, не являющейся функцией инициализации (шага, предиката и финализации) должен быть эффективно идентичным внутреннему списку параметров (V... A...).

Шаг 3: Заполнение пропущенных функций.

  1. Если функция инициализации опущена, используется значение по умолчанию для типа переменной итерации клаузы.
  2. Если функция шага опущена, используется функция тождества типа переменной итерации клаузы; вставляются параметры опущенных аргументов перед параметром функции тождества для не-void переменных итерации предыдущих клауз. (Это сделает переменную цикла локальной инвариантой цикла.)
  3. Если функция предиката опущена, используется константная true функция. (Это позволит циклу продолжаться, насколько это касается данной клаузы. Обратите внимание, что в таких случаях соответствующая функция финализации недоступна.)
  4. Если функция финализации опущена, используется значение по умолчанию для типа возврата цикла.

Шаг 4: Заполнение отсутствующих типов параметров.

  1. На данном этапе каждый список параметров функции инициализации эффективно идентичен внешнему списку параметров (A...), но некоторые списки могут быть короче. Для каждой функции инициализации с коротким списком параметров список дописывается.
  2. На данном этапе каждый список параметров функции, не являющейся функцией инициализации, эффективно идентичен внутреннему списку параметров (V... A...), но некоторые списки могут быть короче. Для каждой функции, не являющейся функцией инициализации, с коротким списком параметров список дописывается.
  3. Списки аргументов дописываются путём удаления неиспользуемых последующих аргументов.

Заключительные замечания.

  1. После этих шагов все пункты были скорректированы путем добавления пропущенных функций и аргументов.
  2. Все функции инициализации имеют общий список типов параметров (A...), который также будет у конечного обработчика цикла.
  3. Все функции завершения имеют общий возвращаемый тип R, который также будет у конечного обработчика цикла.
  4. Все функции, не являющиеся функциями инициализации, имеют общий список типов параметров (V... A...), состоящий из (не-void) переменных итерации V, за которыми следуют параметры цикла.
  5. Каждая пара функций инициализации и шага согласуется по своему возвращаемому типу V.
  6. Каждая функция, не являющаяся функцией инициализации, сможет наблюдать текущие значения (v...) всех переменных итерации.
  7. Каждая функция сможет наблюдать входящие значения (a...) всех параметров цикла.

Пример. Вследствие шага 1А выше, комбинатор loop имеет следующее свойство:

  • Даны N пункты Cn = {null, Sn, Pn} с n = 1..N.
  • Предположим, что обработчики предикатов Pn либо являются null, либо не имеют параметров. (Только один Pn должен быть не-null.)
  • Предположим, что обработчики шагов Sn имеют сигнатуры (B1..BX)Rn, для некоторой константы X>=N.
  • Предположим, что Q - это количество непустых типов Rn, а (V1...VQ) - последовательность этих типов.
  • Должно выполняться условие Vn == Bn для n = 1..min(X,Q).
  • Типы параметров Vn будут интерпретированы как элементы локального состояния цикла (V...).
  • Любые оставшиеся типы BQ+1..BX (если Q<X) будут определять типы параметров результирующего обработчика цикла (A...).
В этом примере параметры обработчика цикла (A...) были получены из функций шагов, что естественно, если большая часть вычислений цикла происходит в шагах. Для некоторых циклов вычислительная нагрузка может быть наибольшей в функциях pred, и поэтому функции pred могут нуждаться в приеме значений параметров цикла. Для циклов со сложной логикой выхода функции fini могут нуждаться в приеме параметров цикла, и то же самое относится к циклам со сложной логикой входа, где функции init потребуют дополнительных параметров. По этим причинам правила определения этих параметров максимально симметричны для всех частей пункта. В общем случае параметры цикла служат общими неизменяемыми значениями во всем цикле, а переменные итерации служат общими изменяемыми значениями или (если нет функции шага) внутренними неизменяемыми временными переменными цикла.

Выполнение цикла.

  1. При вызове цикла входные значения цикла сохраняются в локальных переменных, которые будут переданы каждой функции пункта. Эти локальные переменные неизменны в цикле.
  2. Каждая функция инициализации выполняется в порядке пункта (передача внешних аргументов (a...)) и значения, не являющиеся void, сохраняются (как переменные итерации (v...)) в локальные переменные. Эти локальные переменные будут изменяться в цикле (если их шаги не являются тождественными функциями, как указано выше).
  3. Все вызовы функций (за исключением функций инициализации) будут получать внутренний список параметров, состоящий из значений переменных итерации, не являющихся void (v...) (в порядке пунктов) и затем входных данных цикла (a...) (в порядке аргументов).
  4. Функции шага и предиката затем выполняются в порядке пунктов (шаг перед предикатом), пока функция предиката не вернет false.
  5. Результат вызова функции шага, не являющийся void, используется для обновления соответствующего значения в последовательности (v...) переменных цикла. Обновленное значение немедленно видно всем последующим вызовам функций.
  6. Если функция предиката возвращает false, вызывается соответствующая функция завершения, и возвращаемое значение (типа R) возвращается из цикла в целом.
  7. Если все функции предиката всегда возвращают true, ни одна функция завершения никогда не вызывается, и цикл не может завершиться иначе, как путем выброса исключения.

Рекомендации по использованию.

  • Хотя каждая функция шага получит текущие значения всех переменных цикла, иногда функции шага требуется только наблюдать текущее значение своей переменной. В этом случае функции шага может потребоваться явно отбросить все предшествующие переменные цикла. Это потребует указания их типов в выражении, подобном dropArguments(step, 0, V0.class, ...).
  • Переменные цикла не обязаны изменяться; они могут быть неизменными в цикле. Пункт может создать неизменную переменную цикла с помощью подходящей функции инициализации без функции шага, предиката или завершения. Это может быть полезно для «подключения» входящего аргумента цикла к функции шага или предиката смежной переменной цикла.
  • Если некоторые из функций пункта являются виртуальными методами экземпляра, сам экземпляр можно удобно поместить в начальную неизменную переменную цикла, используя начальный пункт, такой как new MethodHandle[]{identity(ObjType.class)}. В этом случае ссылка на экземпляр будет первым значением переменной итерации, и будет легко использовать виртуальные методы в качестве частей пункта, поскольку все они будут принимать ведущую ссылку на экземпляр, соответствующую этому значению.

Вот псевдокод для результирующего обработчика цикла. Как и выше, V и v представляют типы и значения переменных цикла; A и a представляют аргументы, переданные всему циклу; и R - это общий тип результата всех финализаторов, а также результирующего цикла.


 V... init...(A...);
 boolean pred...(V..., A...);
 V... step...(V..., A...);
 R fini...(V..., A...);
 R loop(A... a) {
   V... v... = init...(a...);
   for (;;) {
     for ((v, p, s, f) in (v..., pred..., step..., fini...)) {
       v = s(v..., a...);
       if (!p(v..., a...)) {
         return f(v..., a...);
       }
     }
   }
 }
 
Обратите внимание, что списки типов параметров (V...) и (A...) были расширены до полной длины, даже если отдельные функции пунктов могут их не принимать все. Как указано выше, пропущенные параметры заполняются так, как будто это делается с помощью dropArgumentsToMatch(MethodHandle, int, List, int).
Примечание API:
Пример:

 // iterative implementation of the factorial function as a loop handle
 static int one(int k) { return 1; }
 static int inc(int i, int acc, int k) { return i + 1; }
 static int mult(int i, int acc, int k) { return i * acc; }
 static boolean pred(int i, int acc, int k) { return i < k; }
 static int fin(int i, int acc, int k) { return acc; }
 // assume MH_one, MH_inc, MH_mult, MH_pred, and MH_fin are handles to the above methods
 // null initializer for counter, should initialize to 0
 MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
 MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
 MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
 assertEquals(120, loop.invoke(5));
 
Тот же пример, отбрасывая аргументы и используя комбинаторы:

 // simplified implementation of the factorial function as a loop handle
 static int inc(int i) { return i + 1; } // drop acc, k
 static int mult(int i, int acc) { return i * acc; } //drop k
 static boolean cmp(int i, int k) { return i < k; }
 // assume MH_inc, MH_mult, and MH_cmp are handles to the above methods
 // null initializer for counter, should initialize to 0
 MethodHandle MH_one = MethodHandles.constant(int.class, 1);
 MethodHandle MH_pred = MethodHandles.dropArguments(MH_cmp, 1, int.class); // drop acc
 MethodHandle MH_fin = MethodHandles.dropArguments(MethodHandles.identity(int.class), 0, int.class); // drop i
 MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
 MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
 MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
 assertEquals(720, loop.invoke(6));
 
Аналогичный пример, используя вспомогательный объект для хранения параметра цикла:

 // instance-based implementation of the factorial function as a loop handle
 static class FacLoop {
   final int k;
   FacLoop(int k) { this.k = k; }
   int inc(int i) { return i + 1; }
   int mult(int i, int acc) { return i * acc; }
   boolean pred(int i) { return i < k; }
   int fin(int i, int acc) { return acc; }
 }
 // assume MH_FacLoop is a handle to the constructor
 // assume MH_inc, MH_mult, MH_pred, and MH_fin are handles to the above methods
 // null initializer for counter, should initialize to 0
 MethodHandle MH_one = MethodHandles.constant(int.class, 1);
 MethodHandle[] instanceClause = new MethodHandle[]{MH_FacLoop};
 MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
 MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
 MethodHandle loop = MethodHandles.loop(instanceClause, counterClause, accumulatorClause);
 assertEquals(5040, loop.invoke(7));
 
Параметры:
clauses - массив массивов (четверок) MethodHandles, соответствующих правилам, описанным выше.
Возвращает:
обработчик метода, воплощающий поведение цикла, как определено аргументами.
Исключения:
IllegalArgumentException - в случае нарушения любого из описанных выше ограничений.
С тех пор:
9
См. также:
  • whileLoop(MethodHandle, MethodHandle, MethodHandle)
  • doWhileLoop(MethodHandle, MethodHandle, MethodHandle)
  • countedLoop(MethodHandle, MethodHandle, MethodHandle)
  • iteratedLoop(MethodHandle, MethodHandle, MethodHandle)

Цикл while

public static MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body)
Создаёт цикл while по инициализатору, телу и предикату. Это удобная оболочка для обобщённого комбинатора циклов.

Обработчик pred описывает условие цикла; а body, его тело. Цикл, полученный с помощью этого метода, в каждой итерации сначала вычисляет предикат, а затем выполняет его тело (если предикат вычисляется как true). Цикл завершится, как только предикат вычислится как false (в этом случае тело не будет выполнено).

Обработчик init описывает начальное значение дополнительной необязательной локальной переменной цикла. В каждой итерации эта локальная переменная цикла, если она присутствует, передаётся в body и обновляется значением, возвращаемым из её вызова. Результатом выполнения цикла будет конечное значение дополнительной локальной переменной цикла (если она присутствует).

Для этих аргументов-обработчиков действуют следующие правила:

  • Обработчик body не должен быть null; его тип должен иметь вид (V A...)V, где V не void, или же (A...)void. (В случае void, мы присваиваем типу void имя V, и будем писать (V A...)V с пониманием, что тип void V тихо удаляется из списка параметров, оставляя (A...)V.)
  • Список параметров (V A...) тела называется внутренним списком параметров. Он будет ограничивать списки параметров других частей цикла.
  • Если тип переменной итерации V удаляется из внутреннего списка параметров, получившийся более короткий список (A...) называется внешним списком параметров.
  • Тип возвращаемого значения тела V, если он не void, определяет тип дополнительной переменной состояния цикла. Тело должно принимать и возвращать значение этого типа V.
  • Если init не null, он должен иметь тип возвращаемого значения V. Его список параметров (некоторого вида (A*)) должен быть эквивалентен внешнему списку параметров (A...).
  • Если init является null, переменная цикла будет инициализирована её стандартным значением.
  • Обработчик pred не должен быть null. Он должен иметь boolean в качестве типа возвращаемого значения. Его список параметров (либо пустой, либо вида (V A*)) должен быть эквивалентен внутреннему списку параметров.

Тип результата и сигнатура параметра полученного обработчика цикла определяются следующим образом:

  • Тип результата обработчика цикла — это тип результата V тела.
  • Типы параметров обработчика цикла — это типы (A...), из внешнего списка параметров.

Вот псевдокод для полученного обработчика цикла. В коде V/v представляют тип/значение единственной переменной цикла, а также тип результата цикла; и A/a, тип/значение аргумента, переданного в цикл.


 V init(A...);
 boolean pred(V, A...);
 V body(V, A...);
 V whileLoop(A... a...) {
   V v = init(a...);
   while (pred(v, a...)) {
     v = body(v, a...);
   }
   return v;
 }
 
Примечание API:
Пример:

 // implement the zip function for lists as a loop handle
 static List<String> initZip(Iterator<String> a, Iterator<String> b) { return new ArrayList<>(); }
 static boolean zipPred(List<String> zip, Iterator<String> a, Iterator<String> b) { return a.hasNext() && b.hasNext(); }
 static List<String> zipStep(List<String> zip, Iterator<String> a, Iterator<String> b) {
   zip.add(a.next());
   zip.add(b.next());
   return zip;
 }
 // assume MH_initZip, MH_zipPred, and MH_zipStep are handles to the above methods
 MethodHandle loop = MethodHandles.whileLoop(MH_initZip, MH_zipPred, MH_zipStep);
 List<String> a = Arrays.asList("a", "b", "c", "d");
 List<String> b = Arrays.asList("e", "f", "g", "h");
 List<String> zipped = Arrays.asList("a", "e", "b", "f", "c", "g", "d", "h");
 assertEquals(zipped, (List<String>) loop.invoke(a.iterator(), b.iterator()));
 
, Реализация этого метода может быть выражена следующим образом:

 MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) {
     MethodHandle fini = (body.type().returnType() == void.class
                         ? null : identity(body.type().returnType()));
     MethodHandle[]
         checkExit = { null, null, pred, fini },
         varBody   = { init, body };
     return loop(checkExit, varBody);
 }
 
Параметры:
init - необязательный инициализатор, предоставляющий начальное значение переменной цикла. Может быть null, подразумевая стандартное начальное значение. Смотрите выше для других ограничений.
pred - условие для цикла, которое не может быть null. Его тип результата должен быть boolean. Смотрите выше для других ограничений.
body - тело цикла, которое не может быть null. Оно управляет параметрами цикла и типом результата. Смотрите выше для других ограничений.
Возвращает:
обработчик метода, реализующий цикл while как описано аргументами.
Использует:
IllegalArgumentException - если правила для аргументов нарушены.
NullPointerException - если pred или body имеют null.
С:
9
См. также:
  • loop(MethodHandle[][])
  • doWhileLoop(MethodHandle, MethodHandle, MethodHandle)

Цикл do-while

public static MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred)
Создаёт цикл do-while по инициализатору, телу и предикату. Это удобная оболочка для обобщённого комбинатора циклов.

Обработчик pred описывает условие цикла; а body, его тело. Цикл, полученный с помощью этого метода, в каждой итерации сначала выполняет его тело, а затем вычисляет предикат. Цикл завершится, как только предикат вычислится как false после выполнения тела.

Обработчик init описывает начальное значение дополнительной необязательной локальной переменной цикла. В каждой итерации эта локальная переменная цикла, если она присутствует, передаётся в body и обновляется значением, возвращаемым из её вызова. Результатом выполнения цикла будет конечное значение дополнительной локальной переменной цикла (если она присутствует).

Для этих аргументов-обработчиков действуют следующие правила:

  • Обработчик body не должен быть null; его тип должен иметь вид (V A...)V, где V не void, или же (A...)void. (В случае void, мы присваиваем типу void имя V, и будем писать (V A...)V с пониманием, что тип void V тихо удаляется из списка параметров, оставляя (A...)V.)
  • Список параметров (V A...) тела называется внутренним списком параметров. Он будет ограничивать списки параметров других частей цикла.
  • Если тип переменной итерации V удаляется из внутреннего списка параметров, получившийся более короткий список (A...) называется внешним списком параметров.
  • Тип возвращаемого значения тела V, если он не void, определяет тип дополнительной переменной состояния цикла. Тело должно принимать и возвращать значение этого типа V.
  • Если init не null, он должен иметь тип возвращаемого значения V. Его список параметров (некоторого вида (A*)) должен быть эквивалентен внешнему списку параметров (A...).
  • Если init является null, переменная цикла будет инициализирована её стандартным значением.
  • Обработчик pred не должен быть null. Он должен иметь boolean в качестве типа возвращаемого значения. Его список параметров (либо пустой, либо вида (V A*)) должен быть эквивалентен внутреннему списку параметров.

Тип результата и сигнатура параметра полученного обработчика цикла определяются следующим образом:

  • Тип результата обработчика цикла — это тип результата V тела.
  • Типы параметров обработчика цикла — это типы (A...), из внешнего списка параметров.

Вот псевдокод для полученного обработчика цикла. В коде V/v представляют тип/значение единственной переменной цикла, а также тип результата цикла; и A/a, тип/значение аргумента, переданного в цикл.


 V init(A...);
 boolean pred(V, A...);
 V body(V, A...);
 V doWhileLoop(A... a...) {
   V v = init(a...);
   do {
     v = body(v, a...);
   } while (pred(v, a...));
   return v;
 }
 
Примечание API:
Пример:

 // int i = 0; while (i < limit) { ++i; } return i; => limit
 static int zero(int limit) { return 0; }
 static int step(int i, int limit) { return i + 1; }
 static boolean pred(int i, int limit) { return i < limit; }
 // assume MH_zero, MH_step, and MH_pred are handles to the above methods
 MethodHandle loop = MethodHandles.doWhileLoop(MH_zero, MH_step, MH_pred);
 assertEquals(23, loop.invoke(23));
 
, Реализация этого метода может быть выражена следующим образом:

 MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) {
     MethodHandle fini = (body.type().returnType() == void.class
                         ? null : identity(body.type().returnType()));
     MethodHandle[] clause = { init, body, pred, fini };
     return loop(clause);
 }
 
Параметры:
init - необязательный инициализатор, предоставляющий начальное значение переменной цикла. Может быть null, подразумевая стандартное начальное значение. Смотрите выше для других ограничений.
body - тело цикла, которое не может быть null. Оно управляет параметрами цикла и типом результата. Смотрите выше для других ограничений.
pred - условие для цикла, которое не может быть null. Его тип результата должен быть boolean. Смотрите выше для других ограничений.
Возвращает:
обработчик метода, реализующий цикл while как описано аргументами.
Использует:
IllegalArgumentException - если правила для аргументов нарушены.
NullPointerException - если pred или body имеют null.
С:
9
См. также:
  • loop(MethodHandle[][])
  • whileLoop(MethodHandle, MethodHandle, MethodHandle)

countedLoop

public static MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body)
Создаёт цикл, который выполняет заданное количество итераций. Это удобная обёртка для общего комбинатора циклов.

Количество итераций определяется результатом оценки iterations обработчика. Счётчик итераций i — это дополнительная переменная цикла итераций типа int. Она будет инициализирована значением 0 и увеличиваться на 1 на каждой итерации.

Если body обработчик возвращает не-void тип V, то присутствует ведущая переменная цикла итераций этого типа. Эта переменная инициализируется с помощью необязательного init обработчика или по умолчанию для типа V, если этот обработчик null.

На каждой итерации переменные итерации передаются в вызов body обработчика. Не-void значение, возвращённое из тела (типа V) обновляет ведущую переменную итерации. Результатом выполнения цикла будет конечное значение V этой переменной (или void если переменной V нет).

Для обработчиков аргументов соблюдаются следующие правила:

  • iterations обработчик не должен быть null, и должен возвращать тип int, который здесь упоминается как I в списках типов параметров.
  • body обработчик не должен быть null; его тип должен иметь вид (V I A...)V, где V не-void, или иначе (I A...)void. (В случае void, мы присваиваем типу void имя V, и мы будем писать (V I A...)V, понимая, что тип void V тихо опускается из списка параметров, оставляя (I A...)V.)
  • Список параметров (V I A...) тела вносит вклад в список типов, называемый внутренним списком параметров. Он будет ограничивать списки параметров других частей цикла.
  • В качестве специального случая, если тело вносит вклад только в типы V и I, без дополнительных типов A, тогда внутренний список параметров дополняется типами аргументов A... iterations обработчика.
  • Если типы переменных итераций (V I) удаляются из внутреннего списка параметров, полученный более короткий список (A...) называется внешним списком параметров.
  • Тип возвращаемого значения тела V, если он не-void, определяет тип дополнительной переменной состояния цикла. Тело должно принимать в качестве параметра ведущее значение и возвращать значение этого типа V.
  • Если init не-null, он должен иметь тип возвращаемого значения V. Его список параметров (некоторой формы (A*)) должен быть эффективно идентичен внешнему списку параметров (A...).
  • Если init null, переменная цикла будет инициализирована своим значением по умолчанию по умолчанию.
  • Список параметров iterations (некоторой формы (A*)) должен быть эффективно идентичен внешнему списку параметров (A...).

Тип возвращаемого значения и сигнатура параметров результирующего обработчика цикла определяются следующим образом:

  • Тип возвращаемого значения обработчика цикла — это тип возвращаемого значения V тела.
  • Типы параметров обработчика цикла — это типы (A...), из внешнего списка параметров.

Вот псевдокод для результирующего обработчика цикла. В коде V/v представляют тип/значение второй переменной цикла, а также тип возвращаемого значения цикла; а A.../a... представляют аргументы, переданные в цикл.


 int iterations(A...);
 V init(A...);
 V body(V, int, A...);
 V countedLoop(A... a...) {
   int end = iterations(a...);
   V v = init(a...);
   for (int i = 0; i < end; ++i) {
     v = body(v, i, a...);
   }
   return v;
 }
 
Примечание API:
Пример со полностью совместимым методом тела:

 // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
 // => a variation on a well known theme
 static String step(String v, int counter, String init) { return "na " + v; }
 // assume MH_step is a handle to the method above
 MethodHandle fit13 = MethodHandles.constant(int.class, 13);
 MethodHandle start = MethodHandles.identity(String.class);
 MethodHandle loop = MethodHandles.countedLoop(fit13, start, MH_step);
 assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
 
, Пример с простейшим возможным типом метода тела и передачей количества итераций в вызов цикла:

 // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
 // => a variation on a well known theme
 static String step(String v, int counter ) { return "na " + v; }
 // assume MH_step is a handle to the method above
 MethodHandle count = MethodHandles.dropArguments(MethodHandles.identity(int.class), 1, String.class);
 MethodHandle start = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, int.class);
 MethodHandle loop = MethodHandles.countedLoop(count, start, MH_step);  // (v, i) -> "na " + v
 assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke(13, "Lambdaman!"));
 
, Пример, который рассматривает количество итераций, строку для добавления и строку для добавления как параметры цикла:

 // String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s;
 // => a variation on a well known theme
 static String step(String v, int counter, int iterations_, String pre, String start_) { return pre + " " + v; }
 // assume MH_step is a handle to the method above
 MethodHandle count = MethodHandles.identity(int.class);
 MethodHandle start = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, int.class, String.class);
 MethodHandle loop = MethodHandles.countedLoop(count, start, MH_step);  // (v, i, _, pre, _) -> pre + " " + v
 assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke(13, "na", "Lambdaman!"));
 
, Пример, который иллюстрирует использование dropArgumentsToMatch(MethodHandle, int, List, int) для принудительного типа цикла:

 // String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s;
 // => a variation on a well known theme
 static String step(String v, int counter, String pre) { return pre + " " + v; }
 // assume MH_step is a handle to the method above
 MethodType loopType = methodType(String.class, String.class, int.class, String.class);
 MethodHandle count = MethodHandles.dropArgumentsToMatch(MethodHandles.identity(int.class),    0, loopType.parameterList(), 1);
 MethodHandle start = MethodHandles.dropArgumentsToMatch(MethodHandles.identity(String.class), 0, loopType.parameterList(), 2);
 MethodHandle body  = MethodHandles.dropArgumentsToMatch(MH_step,                              2, loopType.parameterList(), 0);
 MethodHandle loop = MethodHandles.countedLoop(count, start, body);  // (v, i, pre, _, _) -> pre + " " + v
 assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("na", 13, "Lambdaman!"));
 
, Реализация этого метода может быть выражена следующим образом:

 MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) {
     return countedLoop(empty(iterations.type()), iterations, init, body);
 }
 
Параметры:
iterations - обработчик, возвращающий количество итераций, которые должен выполнить этот цикл. Тип возвращаемого значения обработчика должен быть int. См. выше другие ограничения.
init - необязательный инициализатор, предоставляющий начальное значение переменной цикла. Может быть null, подразумевая значение по умолчанию. См. выше другие ограничения.
body - тело цикла, которое не может быть null. Оно контролирует параметры цикла и тип возвращаемого значения в стандартном случае (см. выше подробности). Оно должно принимать собственный тип возвращаемого значения (если не void) плюс параметр int (для счётчика) и может принимать любое количество дополнительных типов. См. выше другие ограничения.
Возвращает:
обработчик метода, представляющий цикл.
Исключения:
NullPointerException - если любой из iterations или body обработчиков null.
IllegalArgumentException - если какой-либо аргумент нарушает вышеизложенные правила.
С тех пор:
9
См. также:
  • countedLoop(MethodHandle, MethodHandle, MethodHandle, MethodHandle)

countedLoop

public static MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body)
Создаёт цикл, который перебирает диапазон чисел. Это удобная обертка для обобщённого комбинатора циклов.

Счётчик цикла i — это переменная итерации цикла типа int. Обработчики start и end определяют начальное (включительно) и конечное (исключительно) значения счётчика цикла. Счётчик цикла будет инициализирован значением, возвращённым при оценке обработчика start, и будет выполняться до значения, возвращённого обработчиком end (исключительно), с шагом 1.

Если обработчик body возвращает тип, отличный от void типа V, присутствует также ведущая переменная итерации цикла этого типа. Эта переменная инициализируется с помощью необязательного обработчика init или по умолчанию типа V, если этот обработчик null.

В каждой итерации переменные итерации передаются в вызов обработчика body. Значение, отличное от void , возвращаемое телом (типа V) обновляет ведущую переменную итерации. Результатом выполнения обработчика цикла будет окончательное значение V этой переменной (или void, если переменная V отсутствует).

Для обработчиков аргументов соблюдаются следующие правила:

  • Обработчики start и end не должны быть null, и оба должны возвращать общий тип int, обозначаемый здесь как I в списках типов параметров.
  • Обработчик body не должен быть null; его тип должен иметь вид (V I A...)V, где V — не void, или же (I A...)void. (В случае void мы присваиваем тип void имени V, и мы будем писать (V I A...)V с пониманием того, что тип void V тихо удаляется из списка параметров, оставляя (I A...)V.)
  • Список параметров (V I A...) тела вносит вклад в список типов, называемый внутренним списком параметров. Он будет ограничивать списки параметров других частей цикла.
  • В качестве специального случая, если тело вносит вклад только в типы V и I, без дополнительных типов A, тогда внутренний список параметров расширяется типами аргументов A... обработчика end.
  • Если типы переменных итерации (V I) удаляются из внутреннего списка параметров, полученный более короткий список (A...) называется внешним списком параметров.
  • Тип возвращаемого значения тела V, если он не void, определяет тип дополнительной переменной состояния цикла. Тело должно принимать ведущий параметр и возвращать значение этого типа V.
  • Если init не null, оно должно иметь тип возвращаемого значения V. Его список параметров (некоторого вида (A*)) должен быть эффективно идентичен внешнему списку параметров (A...).
  • Если init равно null, переменная цикла будет инициализирована её значением по умолчанию.
  • Список параметров start (некоторого вида (A*) ) должен быть эффективна идентичен внешнему списку параметров (A...).
  • Аналогично, список параметров end должен быть эффективна идентичен внешнему списку параметров.

Тип результата и сигнатура параметров полученного обработчика цикла определяются следующим образом:

  • Тип результата обработчика цикла — это тип результата V тела.
  • Типы параметров обработчика цикла — это типы (A...), из внешнего списка параметров.

Вот псевдокод для полученного обработчика цикла. В коде V/v представляют тип / значение второй переменной цикла, а также тип результата цикла; а A.../a... представляют аргументы, передаваемые в цикл.


 int start(A...);
 int end(A...);
 V init(A...);
 V body(V, int, A...);
 V countedLoop(A... a...) {
   int e = end(a...);
   int s = start(a...);
   V v = init(a...);
   for (int i = s; i < e; ++i) {
     v = body(v, i, a...);
   }
   return v;
 }
 
Примечание API:
Реализация этого метода может быть выражена следующим образом:

 MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) {
     MethodHandle returnVar = dropArguments(identity(init.type().returnType()), 0, int.class, int.class);
     // assume MH_increment and MH_predicate are handles to implementation-internal methods with
     // the following semantics:
     // MH_increment: (int limit, int counter) -> counter + 1
     // MH_predicate: (int limit, int counter) -> counter < limit
     Class<?> counterType = start.type().returnType();  // int
     Class<?> returnType = body.type().returnType();
     MethodHandle incr = MH_increment, pred = MH_predicate, retv = null;
     if (returnType != void.class) {  // ignore the V variable
         incr = dropArguments(incr, 1, returnType);  // (limit, v, i) => (limit, i)
         pred = dropArguments(pred, 1, returnType);  // ditto
         retv = dropArguments(identity(returnType), 0, counterType); // ignore limit
     }
     body = dropArguments(body, 0, counterType);  // ignore the limit variable
     MethodHandle[]
         loopLimit  = { end, null, pred, retv }, // limit = end(); i < limit || return v
         bodyClause = { init, body },            // v = init(); v = body(v, i)
         indexVar   = { start, incr };           // i = start(); i = i + 1
     return loop(loopLimit, bodyClause, indexVar);
 }
 
Параметры:
start — обработчик, возвращающий начальное значение счётчика цикла, который должен быть int. См. выше другие ограничения.
end — обработчик, возвращающий конечное значение счётчика цикла (цикл будет выполняться до end-1). Тип результата должен быть int. См. выше другие ограничения.
init — необязательная инициализация, предоставляющая начальное значение переменной цикла. Может быть null, подразумевая значение по умолчанию. См. выше другие ограничения.
body — тело цикла, которое не может быть null. Оно управляет параметрами цикла и типом результата в стандартном случае (см. подробности выше). Оно должно принимать свой собственный тип возвращаемого значения (если не void) плюс параметр int (для счётчика) и может принимать любое количество дополнительных типов. См. выше другие ограничения.
Возвращает:
обработчик метода, представляющий цикл.
Исключения:
NullPointerException — если любой из обработчиков start, end или body является null.
IllegalArgumentException — если любой из аргументов нарушает сформулированные выше правила.
С:
9
См. также:
  • countedLoop(MethodHandle, MethodHandle, MethodHandle)

iteratedLoop

public static MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body)
Создаёт цикл, который перебирает значения, полученные из Iterator<T>. Это удобная обертка для обобщённого комбинированного оператора цикла.

Сам итератор будет определяться по результату вычисления iterator обработчика. Каждое полученное значение будет храниться в переменной цикла итерации типа T.

Если body обработчик возвращает тип, отличный от void типа V, присутствует также переменная цикла итерации ведущего типа. Эта переменная инициализируется с помощью необязательного init обработчика или значением по умолчанию типа V в случае, если этот обработчик null.

В каждой итерации переменные итерации передаются в вызов body обработчика. Значение, отличное от void типа V, возвращаемое из тела (блока), обновляет ведущую переменную итерации. Результатом выполнения обработчика цикла будет окончательное значение V переменной (или void если нет V переменной).

Для обработчиков аргументов выполняются следующие правила:

  • Обработчик body не должен быть null; его тип должен быть в форме (V T A...)V, где V не является void, или же (T A...)void. (В случае void, мы присваиваем типу void имя V, и будем писать (V T A...)V с пониманием, что тип void типа V тихо удаляется из списка параметров, оставляя (T A...)V.)
  • Список параметров (V T A...) тела добавляется в список типов, называемый внутренним списком параметров. Он будет ограничивать списки параметров других частей цикла.
  • В качестве частного случая, если тело содержит только типы V и T, без дополнительных типов A, то внутренний список параметров дополняется типами аргументов A... обработчика iterator; если он null, добавляется единственный тип Iterable и формирует список A....
  • Если типы переменной итерации (V T) исключаются из внутреннего списка параметров, полученный более короткий список (A...) называется внешним списком параметров.
  • Тип возвращаемого значения тела V, если не void, определяет тип дополнительной переменной состояния цикла. Тело должно принимать ведущий параметр и возвращать значение этого типа V.
  • Если init не null, оно должно иметь тип возвращаемого значения V. Его список параметров (некой формы (A*)) должен быть эквивалентен внешнему списку параметров (A...).
  • Если init равно null, переменная цикла инициализируется своим значением по умолчанию по умолчанию.
  • Если iterator обработчик не null, он должен иметь тип возвращаемого значения java.util.Iterator или подтип. Предполагается, что итератор, который он создаёт во время выполнения цикла, будет возвращать значения, которые могут быть преобразованы к типу T.
  • Список параметров iterator не null (некой формы (A*)) должен быть эквивалентен внешнему списку параметров (A...).
  • Если iterator равно null, по умолчанию устанавливается обработчик метода, который ведёт себя как Iterable.iterator(). В этом случае внутренний список параметров (V T A...) должен содержать по крайней мере один тип A, и параметр обработчика итератора корректируется для приёма ведущего типа A, как будто с помощью метода преобразования asType. Ведущий тип A должен быть Iterable или его подтипом. Эта операция преобразования, выполняемая во время создания цикла, не должна вызывать WrongMethodTypeException.

Тип T может быть либо примитивным, либо ссылочным. Так как тип Iterator<T> стирается в представлении обработчика метода до базового типа Iterator, комбинированный оператор iteratedLoop корректирует ведущий тип аргумента для body на Object, как будто с помощью метода преобразования asType. Поэтому, если во время выполнения цикла обнаружится итератор неправильного типа, могут возникнуть исключения во время выполнения в результате динамических преобразований, выполняемых методом MethodHandle.asType(MethodType).

Тип результата и сигнатура параметров результирующего обработчика цикла определяются следующим образом:

  • Тип результата обработчика цикла — это тип результата V тела.
  • Типы параметров обработчика цикла — это типы (A...), из внешнего списка параметров.

Вот псевдокод результирующего обработчика цикла. В коде V/v представляют тип/значение переменной цикла, а также тип результата цикла; T/t, типы элементов структуры, по которой цикл перебирает; а A.../a... представляют аргументы, передаваемые в цикл.


 Iterator<T> iterator(A...);  // defaults to Iterable::iterator
 V init(A...);
 V body(V,T,A...);
 V iteratedLoop(A... a...) {
   Iterator<T> it = iterator(a...);
   V v = init(a...);
   while (it.hasNext()) {
     T t = it.next();
     v = body(v, t, a...);
   }
   return v;
 }
 
Примечание API:
Пример:

 // get an iterator from a list
 static List<String> reverseStep(List<String> r, String e) {
   r.add(0, e);
   return r;
 }
 static List<String> newArrayList() { return new ArrayList<>(); }
 // assume MH_reverseStep and MH_newArrayList are handles to the above methods
 MethodHandle loop = MethodHandles.iteratedLoop(null, MH_newArrayList, MH_reverseStep);
 List<String> list = Arrays.asList("a", "b", "c", "d", "e");
 List<String> reversedList = Arrays.asList("e", "d", "c", "b", "a");
 assertEquals(reversedList, (List<String>) loop.invoke(list));
 
, Реализация этого метода может быть приблизительно выражена следующим образом:

 MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) {
     // assume MH_next, MH_hasNext, MH_startIter are handles to methods of Iterator/Iterable
     Class<?> returnType = body.type().returnType();
     Class<?> ttype = body.type().parameterType(returnType == void.class ? 0 : 1);
     MethodHandle nextVal = MH_next.asType(MH_next.type().changeReturnType(ttype));
     MethodHandle retv = null, step = body, startIter = iterator;
     if (returnType != void.class) {
         // the simple thing first:  in (I V A...), drop the I to get V
         retv = dropArguments(identity(returnType), 0, Iterator.class);
         // body type signature (V T A...), internal loop types (I V A...)
         step = swapArguments(body, 0, 1);  // swap V <-> T
     }
     if (startIter == null)  startIter = MH_getIter;
     MethodHandle[]
         iterVar    = { startIter, null, MH_hasNext, retv }, // it = iterator; while (it.hasNext())
         bodyClause = { init, filterArguments(step, 0, nextVal) };  // v = body(v, t, a)
     return loop(iterVar, bodyClause);
 }
 
Параметры:
iterator — необязательный обработчик для возврата итератора для запуска цикла. Если не null, обработчик должен возвращать Iterator или подтип. См. выше для других ограничений.
init — необязательный инициализатор, обеспечивающий начальное значение переменной цикла. Может быть null, подразумевая начальное значение по умолчанию. См. выше для других ограничений.
body — тело цикла, которое не может быть null. В стандартном случае он управляет параметрами цикла и типом результата (подробнее см. выше). Он должен принимать свой собственный тип возвращаемого значения (если не void) плюс параметр T (для итерируемых значений) и может принимать любое количество дополнительных типов. См. выше для других ограничений.
Возвращает:
обработчик метода, воплощающий функциональность итерационного цикла.
Исключения:
NullPointerException — если обработчик body null.
IllegalArgumentException — если какой-либо аргумент нарушает указанные выше требования.
См. также:
public static MethodHandle tryFinally(MethodHandle target, MethodHandle cleanup)
С:
9

tryFinally

public static MethodHandle tryFinally(MethodHandle target, MethodHandle cleanup)
Создаёт обработчик метода, адаптирующий обработчик метода target путём обертывания его в блок try-finally. Другой обработчик метода, cleanup, представляет функциональность блока finally. Любое исключение, сгенерированное во время выполнения обработчика target, будет передано обработчику cleanup. Исключение будет повторно выброшено, если обработчик cleanup не выбросит исключение раньше. Значение, возвращённое обработчиком cleanup, будет результатом выполнения обработчика try-finally.

Обработчик cleanup будет получать один или два дополнительных ведущих аргумента. Первый — исключение, выброшенное при выполнении обработчика target, или null, если исключение не было выброшено. Второй — результат выполнения обработчика target, или, если он выбросил исключение, значение по умолчанию нулевого или false типа требуемого типа. Второй аргумент отсутствует, если у обработчика target тип возвращаемого значения void. (Обратите внимание, что, за исключением преобразований типов аргументов, комбинаторы представляют void значения в списках параметров, опуская соответствующие парадоксальные аргументы, а не вставляя null или нулевые значения.)

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

  • тип Throwable, который будет содержать исключение, выброшенное обработчиком target (если таковое имеется); и
  • параметр того же типа, что и тип возвращаемого значения как обработчика target, так и обработчика cleanup, который будет содержать результат выполнения обработчика target. Этот параметр отсутствует, если обработчик target возвращает void.

Псевдокод для результирующего адаптера выглядит следующим образом. В коде V представляет тип возвращаемого значения конструкции try/finally; A/a, типы и значения аргументов для результирующего обработчика, используемого обработкой очистки; и B/b, те же параметры, игнорируемые обработкой очистки.


 V target(A..., B...);
 V cleanup(Throwable, V, A...);
 V adapter(A... a, B... b) {
   V result = (zero value for V);
   Throwable throwable = null;
   try {
     result = target(a..., b...);
   } catch (Throwable t) {
     throwable = t;
     throw t;
   } finally {
     result = cleanup(throwable, result, a...);
   }
   return result;
 }
 

Обратите внимание, что сохранённые аргументы (a... в псевдокоде) не могут быть изменены выполнением целевого обработчика, и поэтому передаются неизменёнными от вызывающего обработчика к обработчику очистки, если он вызывается.

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

Обратите внимание, что tryFinally никогда не преобразует исключения в нормальные возвращаемые значения. В редких случаях, когда исключения необходимо преобразовать таким образом, сначала оберните целевой обработчик с помощью catchException(MethodHandle, Class, MethodHandle), чтобы перехватить исходящее исключение, а затем оберните с tryFinally.

Рекомендуется, чтобы тип первого параметра cleanup был объявлен как Throwable, а не как более узкий подтип. Это гарантирует, что cleanup всегда будет вызываться с любым исключением, которое выбросит target. Объявление более узкого типа может привести к выбросу ClassCastException обработчиком try-finally, если тип исключения, выброшенного target, не может быть приведён к типу первого параметра cleanup. Обратите внимание, что различные типы исключений VirtualMachineError, LinkageError, и RuntimeException могут, в принципе, быть выброшены практически любым Java-кодом, и блок finally, который перехватывает (скажем), только IOException, может замаскировать любые другие за ClassCastException.

Parameters:
target - обработчик, чьё выполнение нужно обернуть в блок try.
cleanup - обработчик, который вызывается в блоке finally.
Returns:
обработчик метода, воплощающий блок try-finally, составленный из двух аргументов.
Throws:
NullPointerException - если какой-либо аргумент равен null
IllegalArgumentException - если cleanup не принимает требуемые ведущие аргументы или если типы обработчиков методов не совпадают по возвращаемым значениям и соответствующим хвостовым параметрам
Since:
9
See Also:
  • catchException(MethodHandle, Class, MethodHandle)

tableSwitch

public static MethodHandle tableSwitch(MethodHandle fallback, MethodHandle... targets)
Создаёт обработчик метода типа переключатель таблиц, который может использоваться для переключения между набором обработчиков методов в зависимости от заданного индексного значения, называемого селектором.

Для значения селектора n, где n попадает в диапазон [0, N), и где N — количество обработчиков методов, обработчик метода типа переключатель таблиц вызовет n-й обработчик метода из списка обработчиков методов.

Для значения селектора, которое не попадает в диапазон [0, N), обработчик метода типа переключатель таблиц вызовет заданный обработчик метода по умолчанию.

Все обработчики методов, переданные в этот метод, должны иметь одинаковый тип, с дополнительным требованием, что ведущий параметр должен быть типа int. Ведущий параметр представляет собой селектор.

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

API Note:
Пример: в каждом случае значение selector выбрасывается, и добавляется дополнительный аргумент String, который конкатенируется (с использованием String.concat(String)) со строкой конкретного метки случая:

 MethodHandles.Lookup lookup = MethodHandles.lookup();
 MethodHandle caseMh = lookup.findVirtual(String.class, "concat",
         MethodType.methodType(String.class, String.class));
 caseMh = MethodHandles.dropArguments(caseMh, 0, int.class);

 MethodHandle caseDefault = MethodHandles.insertArguments(caseMh, 1, "default: ");
 MethodHandle case0 = MethodHandles.insertArguments(caseMh, 1, "case 0: ");
 MethodHandle case1 = MethodHandles.insertArguments(caseMh, 1, "case 1: ");

 MethodHandle mhSwitch = MethodHandles.tableSwitch(
     caseDefault,
     case0,
     case1
 );

 assertEquals("default: data", (String) mhSwitch.invokeExact(-1, "data"));
 assertEquals("case 0: data", (String) mhSwitch.invokeExact(0, "data"));
 assertEquals("case 1: data", (String) mhSwitch.invokeExact(1, "data"));
 assertEquals("default: data", (String) mhSwitch.invokeExact(2, "data"));
 
Parameters:
fallback - обработчик метода по умолчанию, который вызывается, когда селектор не попадает в диапазон [0, N).
targets - массив обработчиков целевых методов.
Returns:
обработчик метода типа переключатель таблиц.
Throws:
NullPointerException - если fallback, массив targets, или любой из элементов массива targets равны null.
IllegalArgumentException - если массив targets пустой, если ведущий параметр обработчика по умолчанию или любого из целевых обработчиков не равен типу int, или если типы обработчика по умолчанию и всех целевых обработчиков не совпадают.

© 1993, 2021, 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/17/docs/api/java.base/java/lang/invoke/MethodHandles.html

Spec-Zone.ru

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