Spec-Zone.ru › OpenJDK 24

Класс MethodHandle

java.lang.Object
java.lang.invoke.MethodHandle
Все реализованные интерфейсы:
Constable
public abstract sealed class MethodHandle extends Object implements Constable
Обработчик методов — это типизированная, непосредственно исполняемая ссылка на базовый метод, конструктор, поле или аналогичную низкоуровневую операцию с опциональными преобразованиями аргументов или возвращаемых значений. Эти преобразования достаточно общие и включают такие шаблоны, как преобразование, вставка, удаление и замену.

Содержимое обработчика методов

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

Каждый обработчик методов сообщает свой описатель типа через type доступ. Этот описатель типа — объект MethodType, структура которого представляет собой серию классов, одним из которых является тип возвращаемого значения метода (или void.class, если его нет).

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

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

Обработчики методов неизменяемы и не имеют видимого состояния. Конечно, они могут быть связаны с базовыми методами или данными, которые демонстрируют состояние. С точки зрения модели памяти Java, любой обработчик методов будет вести себя так, как будто все его (внутренние) поля являются переменными final. Это означает, что любой обработчик методов, который становится видимым для приложения, всегда будет полностью сформирован. Это верно даже в том случае, если обработчик методов публикуется через общую переменную в гонке данных.

Обработчики методов не могут быть подклассифицированы пользователем. Реализации могут (или не могут) создавать внутренние подклассы MethodHandle, которые могут быть видны через операцию Object.getClass. Программист не должен делать выводы о обработчике методов на основе его конкретного класса, так как иерархия классов обработчиков методов (если таковая имеется) может время от времени или в разных реализациях от разных поставщиков изменяться.

Компиляция обработчика методов

Выражение вызова Java метода, называющее invokeExact или invoke, может вызывать обработчик метода из исходного кода Java. С точки зрения исходного кода, эти методы могут принимать любые аргументы, и их результат может быть приведен к любому типу возвращаемого значения. Формально это достигается путем присвоения методам вызова Object типов возвращаемого значения и переменного числа Object аргументов, но они обладают дополнительным свойством, называемым полиморфизмом подписи, которое напрямую связывает эту свободу вызова с исполнением стека JVM.

Как обычно с виртуальными методами, вызовы на уровне исходного кода invokeExact и invoke компилируются в инструкцию invokevirtual. Более необычно, что компилятор должен записывать фактические типы аргументов и не может выполнять преобразования вызовов методов над аргументами. Вместо этого он должен генерировать инструкции, которые помещают их в стек в соответствии с их собственными необработанными типами. Объект обработчика методов сам помещается в стек перед аргументами. Затем компилятор генерирует инструкцию invokevirtual, которая вызывает обработчик метода с символьным описателем типа, описывающим типы аргументов и возвращаемого значения.

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

В качестве частного случая, неприведенному null аргументу присваивается символьный описатель типа java.lang.Void. Неопределенность с типом Void безвредна, так как нет ссылок типа Void, кроме ссылки null.

Вызов обработчика методов

В первый раз при выполнении инструкции invokevirtual она связывается путем символического разрешения имен в инструкции и проверки того, что вызов метода статически допустим. Это также относится к вызовам invokeExact и invoke. В этом случае символьный описатель типа, выданный компилятором, проверяется на правильность синтаксиса, а имена, которые он содержит, разрешаются. Таким образом, инструкция invokevirtual, которая вызывает обработчик метода, всегда будет связана, если символьный описатель типа синтаксически корректен и типы существуют.

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

В случае invokeExact описатель типа вызова (после разрешения символьных имен типов) должен точно соответствовать типу метода принимающего обработчика метода. В случае простого, неточного invoke, разрешенный описатель типа должен быть допустимым аргументом для метода asType получателя. Таким образом, простой invoke более допускает, чем invokeExact.

После проверки типов вызов invokeExact напрямую и немедленно вызывает базовый метод обработчика методов (или другое поведение, как это определено).

Вызов простого invoke работает так же, как и вызов invokeExact, если символьный описатель типа, указанный вызывающим методом, точно соответствует собственному типу обработчика методов. Если тип не совпадает, invoke пытается скорректировать тип получающего обработчика методов, как если бы это был вызов asType, чтобы получить точно вызываемый обработчик метода M2. Это позволяет более эффективно согласовывать тип метода между вызывающим и вызываемым методами.

(Примечание: скорректированный обработчик методов M2 не наблюдается напрямую, и поэтому реализации не обязаны его материализовывать.)

Проверка вызова

В типичных программах соответствие типов обработчика методов обычно успешно проходит. Но если соответствие не найдено, JVM бросит исключение WrongMethodTypeException, либо непосредственно (в случае invokeExact), либо косвенно, как если бы произошел неудачный вызов asType (в случае invoke).

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

Поскольку типы методов содержат «живые» Class объекты, проверка соответствия типов учитывает как имена типов, так и загрузчики классов. Таким образом, даже если обработчик метода M создан в одном загрузчике классов L1 и используется в другом L2, вызовы обработчиков методов являются безопасными с точки зрения типов, поскольку символьный описатель типа вызывающего метода, как разрешен в L2, сопоставляется с оригинальным символьным описателем типа метода вызываемого метода, как разрешенным в L1. Разрешение в L1 происходит при создании M и присвоении его типу, в то время как разрешение в L2 происходит при связывании инструкции invokevirtual.

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

В отличие от API Core Reflection, где проверка доступа выполняется каждый раз при вызове рефлексивного метода, проверка доступа к обработчику метода выполняется при создании обработчика метода. В случае ldc (см. ниже), проверка доступа выполняется в процессе связывания записи константного пула, лежащей в основе константного обработчика метода.

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

Создание обработчика методов

Код Java может создать обработчик метода, который напрямую обращается к любому методу, конструктору или полю, доступ к которым у этого кода есть. Это делается через рефлексивный, основанный на возможностях API, называемый MethodHandles.Lookup. Например, статический обработчик метода может быть получен из Lookup.findStatic. Также существуют методы преобразования из объектов API Core Reflection, такие как Lookup.unreflect.

Как классы и строки, обработчики методов, соответствующие доступным полям, методам и конструкторам, также могут быть представлены непосредственно в константном пуле файла класса в виде констант, которые должны загружаться инструкциями байт-кода ldc. Новый тип записи константного пула, CONSTANT_MethodHandle, напрямую ссылается на ассоциированную запись константного пула CONSTANT_Methodref, CONSTANT_InterfaceMethodref или CONSTANT_Fieldref. (Для получения полной информации о константах обработчиков методов см. разделы 4.4.8 и 5.4.3.5 спецификации виртуальной машины Java.)

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

Ссылка на метод может ссылаться либо на статический, либо на нестатический метод. В нестатическом случае тип дескриптора метода включает явный аргумент получателя, добавленный перед любыми другими аргументами. В типе дескриптора метода начальный аргумент получателя типизируется в соответствии с классом, в котором метод был первоначально запрошен. (Например, если дескриптор нестатического метода получен через ldc, тип получателя — это класс, указанный в записи константного пула.)

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

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

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

Также можно создать дескриптор невиртуального метода для конкретной реализации виртуального метода. Они не выполняют виртуальный поиск, основанный на типе получателя. Такой дескриптор метода имитирует действие инструкции invokespecial для того же метода. Дескриптор невиртуального метода также может быть создан для имитации действия инструкции invokevirtual или invokeinterface над закрытым методом (если это применимо).

Примеры использования

Вот несколько примеров использования:
Object x, y; String s; int i;
MethodType mt; MethodHandle mh;
MethodHandles.Lookup lookup = MethodHandles.lookup();
// mt is (char,char)String
mt = MethodType.methodType(String.class, char.class, char.class);
mh = lookup.findVirtual(String.class, "replace", mt);
s = (String) mh.invokeExact("daddy",'d','n');
// invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
assertEquals(s, "nanny");
// weakly typed invocation (using MHs.invoke)
s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
assertEquals(s, "savvy");
// mt is (Object[])List
mt = MethodType.methodType(java.util.List.class, Object[].class);
mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
assert(mh.isVarargsCollector());
x = mh.invoke("one", "two");
// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
assertEquals(x, java.util.Arrays.asList("one","two"));
// mt is (Object,Object,Object)Object
mt = MethodType.genericMethodType(3);
mh = mh.asType(mt);
x = mh.invokeExact((Object)1, (Object)2, (Object)3);
// invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
assertEquals(x, java.util.Arrays.asList(1,2,3));
// mt is ()int
mt = MethodType.methodType(int.class);
mh = lookup.findVirtual(java.util.List.class, "size", mt);
i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3));
// invokeExact(Ljava/util/List;)I
assert(i == 3);
mt = MethodType.methodType(void.class, String.class);
mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt);
mh.invokeExact(System.out, "Hello, world.");
// invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
Каждый из вышеперечисленных вызовов invokeExact или просто invoke генерирует одну инструкцию invokevirtual с указанным в следующем комментарии символическим описанием типа. В этих примерах предполагается, что вспомогательный метод assertEquals — это метод, который вызывает Objects.equals для своих аргументов и утверждает, что результат равен true.

Исключения

Методы invokeExact и invoke объявлены как выбрасывающие Throwable, что означает отсутствие статических ограничений на то, что может выбросить дескриптор метода. Поскольку JVM не различает обработанные и необработанные исключения (кроме как по их классу, конечно), нет никакого особого влияния на форму байт-кода от приписывания обработанных исключений вызовам дескрипторов методов. Но в исходном коде Java методы, которые выполняют вызовы дескрипторов методов, должны либо явно выбрасывать Throwable, либо перехватывать все исключения локально, повторно выбрасывая только те, которые законны в данном контексте, и обертывая те, которые не являются законными.

Полиморфизм сигнатур

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

В исходном коде вызов метода с полиморфизмом сигнатур будет скомпилирован независимо от запрашиваемого символического описания типа. Как обычно, компилятор Java генерирует инструкцию invokevirtual с заданным символическим описанием типа для указанного метода. Необычная часть заключается в том, что символическое описание типа получено из фактических типов аргументов и возвращаемых значений, а не из объявления метода.

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

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

Взаимодействие между дескрипторами методов и основным API рефлексии

Используя фабричные методы в Lookup API, любой член класса, представленный объектом основного API рефлексии, может быть преобразован в функционально эквивалентный дескриптор метода. Например, рефлексивный Method может быть преобразован в дескриптор метода с помощью Lookup.unreflect. Полученные дескрипторы методов, как правило, обеспечивают более прямой и эффективный доступ к базовым членам класса.

В качестве специального случая, когда используется основной API рефлексии для просмотра методов с полиморфизмом сигнатур invokeExact или просто invoke в этом классе, они отображаются как обычные методы без полиморфизма. Их рефлексивное представление, как отображается Class.getDeclaredMethod, не зависит от их специального статуса в этом API. Например, Method.getModifiers будет сообщать именно те биты модификаторов, которые требуются для любого аналогично объявленного метода, включая в данном случае биты native и varargs.

Как и любой отраженный метод, эти методы (при отражении) могут вызываться с помощью java.lang.reflect.Method.invoke. Однако такие рефлексивные вызовы не приводят к вызовам дескрипторов методов. Такой вызов, если ему передать необходимый аргумент (один, типа Object[]), проигнорирует аргумент и выбросит UnsupportedOperationException.

Поскольку инструкции invokevirtual могут напрямую вызывать дескрипторы методов с любым символическим описанием типа, это рефлексивное представление конфликтует с нормальным представлением этих методов через байт-код. Таким образом, эти два метода нативного кода при рефлексивном просмотре с помощью Class.getDeclaredMethod могут рассматриваться только как заглушки.

Чтобы получить вызывающий метод для конкретного описания типа, используйте MethodHandles.exactInvoker или MethodHandles.invoker. API Lookup.findVirtual также может вернуть дескриптор метода для вызова invokeExact или просто invoke для любого указанного описания типа.

Взаимодействие между дескрипторами методов и Java-генериками

Дескриптор метода можно получить для метода, конструктора или поля, объявленного с использованием Java-генериков. Как и в случае с основным API рефлексии, тип дескриптора метода будет построен из стирания типа уровня исходного кода. При вызове дескриптора метода типы его аргументов или тип возвращаемого значения могут быть генерическими типами или экземплярами типов. Если это произойдет, компилятор заменит эти типы их стиранием при построении символического описания типа для инструкции invokevirtual.

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

  • Типы методов охватывают все возможные арности, от отсутствия аргументов до максимального числа разрешенных аргументов. Генерики не являются вариативными и поэтому не могут этого представить.
  • Типы методов могут указывать аргументы примитивных типов, которые Java-генерические типы не могут охватывать.
  • Функции более высокого порядка над дескрипторами методов (комбинаторы) часто являются генерическими по широкому спектру типов функций, включая те, которые имеют несколько арностей. Невозможно представить такую общность с параметром типа Java.

Ограничения арности

JVM накладывает на все методы и конструкторы любого типа абсолютное ограничение в 255 стековых аргументов. Это ограничение может казаться более ограничительным в некоторых случаях:
  • Аргумент long или double учитывается (для целей ограничений арности) как два слота аргументов.
  • Нестатический метод потребляет дополнительный аргумент для объекта, на котором вызывается метод.
  • Конструктор потребляет дополнительный аргумент для объекта, который создается.
  • Поскольку метод invoke (или другой метод с полиморфизмом сигнатур) дескриптора метода не является виртуальным, он потребляет дополнительный аргумент для самого дескриптора метода, помимо любого объекта-получателя, не являющегося виртуальным.
Эти ограничения подразумевают, что некоторые дескрипторы методов не могут быть созданы только из-за ограничения JVM на стековые аргументы. Например, если статический метод JVM принимает ровно 255 аргументов, для него нельзя создать дескриптор метода. Попытки создания дескрипторов методов с невозможными типами методов приводят к IllegalArgumentException. В частности, тип дескриптора метода не должен иметь арность ровно максимального значения 255.
С момента:
1.7
См. также:
  • MethodType
  • MethodHandles

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

Модификатор и тип Метод Описание
MethodHandle asCollector(int collectArgPos, Class<?> arrayType, int arrayLength)
Создаёт обработку метода, собирающего массив, которая принимает заданное количество позиционных аргументов, начиная с заданной позиции, и собирает их в массив аргументов.
MethodHandle asCollector(Class<?> arrayType, int arrayLength)
Создаёт обработку метода, собирающего массив, которая принимает заданное количество заключительных позиционных аргументов и собирает их в массив аргументов.
MethodHandle asFixedArity()
Создаёт обработку метода фиксированной арности, которая в противном случае эквивалентна текущей обработке метода.
MethodHandle asSpreader(int spreadArgPos, Class<?> arrayType, int arrayLength)
Создаёт обработку метода, распространяющего массив, которая принимает массив аргументов в заданной позиции и распространяет его элементы как позиционные аргументы вместо массива.
MethodHandle asSpreader(Class<?> arrayType, int arrayLength)
Создаёт обработку метода, распространяющего массив, которая принимает заключительный массив аргументов и распространяет его элементы как позиционные аргументы.
final MethodHandle asType(MethodType newType)
Создаёт адаптер обработки метода, который адаптирует тип текущей обработки метода к новому типу.
MethodHandle asVarargsCollector(Class<?> arrayType)
Создаёт адаптер переменной арности, способный принимать любое количество заключительных позиционных аргументов и собирать их в массив аргументов.
MethodHandle bindTo(Object x)
Связывает значение x с первым аргументом метода обработки, без его вызова.
Optional<MethodHandleDesc> describeConstable()
Возвращает номинальный дескриптор для этого экземпляра, если его можно создать, или пустой Optional, если его нельзя создать.
final Object invoke(Object... args)
Вызывает обработку метода, позволяя использовать дескриптор типа любого вызывающего объекта и при необходимости выполняя преобразования над аргументами и возвращаемыми значениями.
final Object invokeExact(Object... args)
Вызывает обработку метода, позволяя использовать дескриптор типа любого вызывающего объекта, но требуя точного соответствия типа.
Object invokeWithArguments(Object... arguments)
Выполняет вызов переменной арности, передавая аргументы в заданном массиве в обработку метода, как если бы это происходило через неточный invoke из места вызова, которое упоминает только тип Object, а фактическое количество аргументов равно длине массива аргументов.
Object invokeWithArguments(List<?> arguments)
Выполняет вызов переменной арности, передавая аргументы в заданном списке в обработку метода, как если бы это происходило через неточный invoke из места вызова, которое упоминает только тип Object, а фактическое количество аргументов равно длине списка аргументов.
boolean isVarargsCollector()
Определяет, поддерживает ли эта обработка метода вызовы переменной арности .
String toString()
Возвращает строковое представление обработки метода, начинающееся со строки "MethodHandle" и заканчивающееся строковым представлением типа обработки метода.
MethodType type()
Сообщает тип этой обработки метода.
MethodHandle withVarargs(boolean makeVarargs)
Адаптирует эту обработку метода к использованию переменной арности, если флаг boolean равен true, в противном случае фиксированной арности.

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

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

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

type

public MethodType type()
Сообщает о типе этого обработчика метода. Каждое обращение к этому обработчику метода посредством invokeExact должно точно соответствовать этому типу.
Возвращает:
тип обработчика метода

invokeExact

public final Object invokeExact(Object... args) throws Throwable
Вызывает обработчик метода, позволяя использовать любой тип вызывающего описателя, но требуя точного соответствия типов. Символический описатель типа в месте вызова invokeExact должен точно соответствовать описателю типа обработчика метода type. Преобразования аргументов или возвращаемых значений не допускаются.

При наблюдении этого метода через API ядра отражения он будет представлен как единственный нативный метод, принимающий массив объектов и возвращающий объект. Если этот нативный метод вызывается напрямую через java.lang.reflect.Method.invoke, через JNI или косвенно через Lookup.unreflect, будет брошено исключение UnsupportedOperationException.

Параметры:
args - список параметров с полиморфизмом сигнатуры, статически представленный с помощью varargs
Возвращает:
результат с полиморфизмом сигнатуры, статически представленный с помощью Object
Исключение:
WrongMethodTypeException - если тип целевого объекта не идентичен символьному описателю типа вызывающего объекта
Throwable - любое исключение, сгенерированное целевым методом, передаётся без изменений через вызов обработчика метода

invoke

public final Object invoke(Object... args) throws Throwable
Вызывает обработчик метода, позволяя использовать любой тип вызывающего описателя и необязательно выполняя преобразования аргументов и возвращаемых значений.

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

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

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

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

При наблюдении этого метода через API ядра отражения он будет представлен как единственный нативный метод, принимающий массив объектов и возвращающий объект. Если этот нативный метод вызывается напрямую через java.lang.reflect.Method.invoke, через JNI или косвенно через Lookup.unreflect, будет брошено исключение UnsupportedOperationException.

Параметры:
args - список параметров с полиморфизмом сигнатуры, статически представленный с помощью varargs
Возвращает:
результат с полиморфизмом сигнатуры, статически представленный с помощью Object
Исключение:
WrongMethodTypeException - если тип целевого объекта не может быть изменён для соответствия символьному описателю типа вызывающего объекта
ClassCastException - если тип целевого объекта может быть изменён для соответствия вызывающему объекту, но преобразование ссылки не удаётся
Throwable - любое исключение, сгенерированное целевым методом, передаётся без изменений через вызов обработчика метода

invokeWithArguments

public Object invokeWithArguments(Object... arguments) throws Throwable
Выполняет вызов с переменным числом аргументов, передавая аргументы в заданном массиве обработчику метода, как если бы он осуществлялся через неточный вызов invoke из места вызова, в котором упоминается только тип Object, а фактическое количество аргументов равно длине массива аргументов.

Выполнение происходит примерно по следующим этапам, хотя методы не гарантированы для вызова, если JVM может предсказать их эффект.

  • Определение длины массива аргументов как N. Для нулевой ссылки, N=0.
  • Сборка элементов массива как логического списка аргументов, каждый из которых статически типизирован как Object.
  • Определение количества параметров типа этого обработчика метода, как M.
  • Определение общего типа TN аргументов N или аргументов M, если меньше N, как TN=MethodType.genericMethodType(Math.min(N, M)).
  • Если N больше M, выполняется проверка и действия для сокращения логического списка аргументов:
    • Проверка, что этот обработчик метода имеет переменное число аргументов с конечным параметром некоторого массива типа A[]. В противном случае вызов завершается с WrongMethodTypeException.
    • Сборка заключительных элементов (их N-M+1) из логического списка аргументов в один массив типа A[], используя преобразования asType для преобразования каждого заключительного аргумента в тип A.
    • Если любое из этих преобразований невозможно, вызов завершается либо с ClassCastException, если какой-либо заключительный элемент не может быть преобразован в A, или с NullPointerException, если какой-либо заключительный элемент является null, и A не является типом ссылки.
    • Замена логических аргументов, собранных в массив типа A[], самим массивом, сокращая список аргументов до длины M. Этот заключительный аргумент сохраняет статический тип A[].
    • Изменение типа TN путём изменения типа N-го параметра с Object на A[].
  • Приведение исходного целевого обработчика метода MH0 к необходимому типу, как MH1 = MH0.asType(TN).
  • Распространение списка аргументов в N отдельных аргументов A0, ....
  • Вызов обработчика метода с изменённым типом на распакованных аргументах: MH1.invokeExact(A0, ...).
  • Получение возвращаемого значения как ссылки Object.

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

В отличие от режима вызова generic, который может «переиспользовать» массив аргументов, передавая его напрямую целевому методу, этот режим вызова всегда создаёт новый параметр массива, даже если исходный массив, переданный invokeWithArguments, был бы приемлем как прямой аргумент целевого метода. Даже если число M фактических аргументов равно количеству аргументов N, и последний аргумент динамически является подходящим массивом типа A[], он всё равно будет упакован в новый массив с одним элементом, так как место вызова статически типизирует аргумент как Object, а не как тип массива. Это не специальное правило для этого метода, а скорее обычный эффект правил вызова с переменным числом аргументов .

Из-за действия шага asType, по мере необходимости применяются следующие преобразования аргументов:

  • преобразование ссылки
  • распаковка
  • расширяющие преобразования примитивов
  • преобразование переменного числа аргументов

Возвращаемое значение вызова упаковывается, если это примитив, или приводится к null, если возвращаемый тип — void.

В отличие от методов с полиморфизмом сигнатуры invokeExact и invoke, invokeWithArguments можно получить обычным способом через API ядра отражения и JNI. Поэтому он может быть использован как мост между нативным или рефлексивным кодом и обработчиками методов.

Примечание API:
Этот вызов примерно эквивалентен следующему коду:
// for jumbo argument lists, adapt varargs explicitly:
int N = (arguments == null? 0: arguments.length);
int M = this.type.parameterCount();
int MAX_SAFE = 127;  // 127 longs require 254 slots, which is OK
if (N > MAX_SAFE && N > M && this.isVarargsCollector()) {
  Class<?> arrayType = this.type().lastParameterType();
  Class<?> elemType = arrayType.getComponentType();
  if (elemType != null) {
    Object args2 = Array.newInstance(elemType, M);
    MethodHandle arraySetter = MethodHandles.arrayElementSetter(arrayType);
    for (int i = 0; i < M; i++) {
      arraySetter.invoke(args2, i, arguments[M-1 + i]);
    }
    arguments = Arrays.copyOf(arguments, M);
    arguments[M-1] = args2;
    return this.asFixedArity().invokeWithArguments(arguments);
  }
} // done with explicit varargs processing

// Handle fixed arity and non-jumbo variable arity invocation.
MethodHandle invoker = MethodHandles.spreadInvoker(this.type(), 0);
Object result = invoker.invokeExact(this, arguments);
Параметры:
arguments - аргументы, которые необходимо передать целевому объекту
Возвращает:
результат, возвращённый целевым объектом
Исключение:
ClassCastException - если аргумент не может быть преобразован с помощью преобразования ссылки
WrongMethodTypeException - если тип целевого объекта не может быть изменён для приёма заданного количества Object аргументов
Throwable - любое исключение, сгенерированное при вызове целевого метода
См. также:
  • MethodHandles.spreadInvoker(java.lang.invoke.MethodType, int)

вызватьСАргументами

public Object invokeWithArguments(List<?> arguments) throws Throwable
Выполняет вызов с переменным числом аргументов, передавая аргументы из данного списка методу-обработчику, как если бы это происходило через неточные invoke из места вызова, которое упоминает только тип Object, и количество фактических аргументов равно длине списка аргументов.

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

  invokeWithArguments(arguments.toArray())

Допустимы списки большого размера, если у этого метода-обработчика переменное количество аргументов. Подробности см. в invokeWithArguments(Object[]).

Параметры:
arguments - аргументы для передачи целевому объекту
Возвращает:
результат, возвращённый целевым объектом
Издаёт исключения:
NullPointerException - если arguments является ссылкой на null
ClassCastException - если аргумент не может быть преобразован при помощи приведения типов ссылок
WrongMethodTypeException - если тип целевого объекта не может быть изменён для приёма заданного количества Object аргументов
Throwable - любое исключение, сгенерированное вызовом целевого метода

asType

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

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

Новый метод-обработчик при вызове выполнит следующие шаги:

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

Этот метод обеспечивает ключевое поведенческое различие между invokeExact и обычным, неточным invoke. Два метода выполняют те же шаги, когда тип вызывающего точно совпадает с типом вызываемого, но когда типы отличаются, обычный invoke также вызывает asType (или какой-либо внутренний эквивалент), чтобы согласовать типы вызывающего и вызываемого.

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

Пусть T0 и T1 — это соответствующие новые и старые типы параметров или старые и новые типы возвращаемых значений. В частности, для некоторого допустимого индекса i, пусть T0=newType.parameterType(i) и T1=this.type().parameterType(i). Или же, двигаясь в другом направлении для возвращаемых значений, пусть T0=this.type().returnType() и T1=newType.returnType(). Если типы одинаковы, новый метод-обработчик не вносит изменений в соответствующий аргумент или возвращаемое значение (если таковое есть). В противном случае, если возможно, применяется одно из следующих преобразований:

  • Если T0 и T1 — ссылки, то применяется приведение типа к T1. (Типы не обязательно должны быть связаны каким-либо определённым образом. Это связано с тем, что динамическое значение null может быть преобразовано в любой тип ссылки.)
  • Если T0 и T1 — примитивные типы, то применяется преобразование вызова Java-метода (JLS 5.3), если оно существует. (В частности, T0 должен преобразовываться в T1 с помощью расширяющего преобразования примитивного типа.)
  • Если T0 — примитивный тип, а T1 — ссылка, применяется преобразование приведения Java (JLS 5.5), если оно существует. (В частности, значение упаковывается из T0 в его оберточное класс, который затем расширяется по мере необходимости до T1.)
  • Если T0 — ссылка, а T1 — примитивный тип, в runtime будет применено распаковывание, возможно, за которым последует преобразование вызова Java-метода (JLS 5.3) над примитивным значением. (Это расширяющие преобразования примитивных типов.) T0 должен быть оберточным классом или его супертипом. (В случае, когда T0 — это Object, это преобразования, разрешённые java.lang.reflect.Method.invoke.) Преобразование распаковки должно иметь возможность успеха, что означает, что если T0 сам по себе не является оберточным классом, должен существовать по крайней мере один оберточный класс TW, являющийся подтипом T0, и значение примитивного типа которого, полученное в результате распаковки, может быть расширено до T1.
  • Если тип возвращаемого значения T1 помечен как void, любое возвращаемое значение отбрасывается
  • Если тип возвращаемого значения T0 — void, а T1 — ссылка, вводится значение null.
  • Если тип возвращаемого значения T0 — void, а T1 — примитивный тип, вводится нулевое значение.
(Примечание: Как T0, так и T1 могут рассматриваться как статические типы, так как ни один из них не соответствует конкретно динамическому типу какого-либо фактического аргумента или возвращаемого значения.)

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

Во время выполнения преобразования, применяемые к аргументам или возвращаемым значениям ссылочного типа, могут потребовать дополнительных проверок во время выполнения, которые могут завершиться ошибкой. Операция распаковки может завершиться ошибкой, потому что исходная ссылка равна null, что приведёт к NullPointerException. Операция распаковки или приведение типа ссылки также могут завершиться ошибкой при ссылке на объект неверного типа, что приведёт к ClassCastException. Хотя операция распаковки может принимать несколько типов обёрток, если ни одна из них недоступна, будет выброшено ClassCastException.

Параметры:
newType - ожидаемый тип нового метода-обработчика
Возвращает:
метод-обработчик, делегирующий this после выполнения необходимых преобразований аргументов и организации необходимых преобразований возвращаемых значений
Издаёт исключения:
NullPointerException - если newType является ссылкой на null
WrongMethodTypeException - если преобразование не может быть выполнено
См. также:
  • MethodHandles.explicitCastArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)

asSpreader

public MethodHandle asSpreader(Class<?> arrayType, int arrayLength)
Создаёт метод-обработчик с разбросом массива, который принимает аргумент массива в конце и разбрасывает его элементы в качестве позиционных аргументов. Новый метод-обработчик адаптирует текущий метод-обработчик, как свой целевой объект. Тип адаптера будет таким же, как и тип целевого объекта, за исключением того, что последние arrayLength параметры типа целевого объекта заменяются одним параметром массива типа arrayType.

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

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

Перед вызовом целевого объекта адаптер проверяет, что массив содержит ровно достаточно элементов для предоставления правильного количества аргументов методу-обработчику целевого объекта. (Массив также может быть null, когда требуется ноль элементов.)

Когда адаптер вызывается, длина предоставленного array аргумента запрашивается так, как будто это делается с помощью array.length или arraylength байткода. Если адаптер принимает аргумент массива с нулевой длиной, предоставленный array аргумент может быть либо массивом нулевой длины, либо null; в противном случае адаптер выбросит NullPointerException, если массив null, и выбросит IllegalArgumentException, если массив не имеет необходимого количества элементов.

Вот несколько простых примеров методов-обработчиков с разбросом массивов:

MethodHandle equals = publicLookup()
  .findVirtual(String.class, "equals", methodType(boolean.class, Object.class));
assert( (boolean) equals.invokeExact("me", (Object)"me"));
assert(!(boolean) equals.invokeExact("me", (Object)"thee"));
// spread both arguments from a 2-array:
MethodHandle eq2 = equals.asSpreader(Object[].class, 2);
assert( (boolean) eq2.invokeExact(new Object[]{ "me", "me" }));
assert(!(boolean) eq2.invokeExact(new Object[]{ "me", "thee" }));
// try to spread from anything but a 2-array:
for (int n = 0; n <= 10; n++) {
  Object[] badArityArgs = (n == 2 ? new Object[0] : new Object[n]);
  try { assert((boolean) eq2.invokeExact(badArityArgs) && false); }
  catch (IllegalArgumentException ex) { } // OK
}
// spread both arguments from a String array:
MethodHandle eq2s = equals.asSpreader(String[].class, 2);
assert( (boolean) eq2s.invokeExact(new String[]{ "me", "me" }));
assert(!(boolean) eq2s.invokeExact(new String[]{ "me", "thee" }));
// spread second arguments from a 1-array:
MethodHandle eq1 = equals.asSpreader(Object[].class, 1);
assert( (boolean) eq1.invokeExact("me", new Object[]{ "me" }));
assert(!(boolean) eq1.invokeExact("me", new Object[]{ "thee" }));
// spread no arguments from a 0-array or null:
MethodHandle eq0 = equals.asSpreader(Object[].class, 0);
assert( (boolean) eq0.invokeExact("me", (Object)"me", new Object[0]));
assert(!(boolean) eq0.invokeExact("me", (Object)"thee", (Object[])null));
// asSpreader and asCollector are approximate inverses:
for (int n = 0; n <= 2; n++) {
    for (Class<?> a : new Class<?>[]{Object[].class, String[].class, CharSequence[].class}) {
        MethodHandle equals2 = equals.asSpreader(a, n).asCollector(a, n);
        assert( (boolean) equals2.invokeWithArguments("me", "me"));
        assert(!(boolean) equals2.invokeWithArguments("me", "thee"));
    }
}
MethodHandle caToString = publicLookup()
  .findStatic(Arrays.class, "toString", methodType(String.class, char[].class));
assertEquals("[A, B, C]", (String) caToString.invokeExact("ABC".toCharArray()));
MethodHandle caString3 = caToString.asCollector(char[].class, 3);
assertEquals("[A, B, C]", (String) caString3.invokeExact('A', 'B', 'C'));
MethodHandle caToString2 = caString3.asSpreader(char[].class, 2);
assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray()));
Параметры:
arrayType - обычно Object[], тип аргумента массива, из которого извлекать разбросанные аргументы
arrayLength - количество аргументов для разброса из входящего аргумента массива
Возвращает:
новый метод-обработчик, который разбрасывает свой конечный аргумент массива, прежде чем вызвать исходный метод-обработчик
Издаёт исключения:
NullPointerException - если arrayType является ссылкой на null
IllegalArgumentException - если arrayType не является типом массива или если целевой объект не имеет хотя бы arrayLength типов параметров, или если arrayLength отрицательно, или если тип результирующего метода-обработчика будет иметь слишком много параметров
WrongMethodTypeException - если подразумеваемый asType вызов завершается ошибкой
См. также:
  • asCollector(java.lang.Class<?>, int)

asSpreader

public MethodHandle asSpreader(int spreadArgPos, Class<?> arrayType, int arrayLength)
Создаёт метод-обработчик с распределением массива, который принимает массив в заданной позиции и распределяет его элементы в качестве позиционных аргументов вместо массива. Новый обработчик метода адаптирует текущий обработчик метода в качестве цели. Тип адаптера будет таким же, как тип цели, за исключением того, что arrayLength параметры типа цели, начиная с позиции spreadArgPos, заменяются одним параметром массива типа arrayType.

Этот метод очень похож на asSpreader(Class, int), но принимает дополнительный spreadArgPos аргумент, чтобы указать, в какой позиции списка параметров должно происходить распространение.

Примечание API:
Пример:
   MethodHandle compare = LOOKUP.findStatic(Objects.class, "compare", methodType(int.class, Object.class, Object.class, Comparator.class));
   MethodHandle compare2FromArray = compare.asSpreader(0, Object[].class, 2);
   Object[] ints = new Object[]{3, 9, 7, 7};
   Comparator<Integer> cmp = (a, b) -> a - b;
   assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 0, 2), cmp) < 0);
   assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 1, 3), cmp) > 0);
   assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 2, 4), cmp) == 0);
Параметры:
spreadArgPos — позиция (индекс, начинающийся с нуля) в списке аргументов, с которой должно начинаться распространение.
arrayType — обычно Object[], тип массива-аргумента, из которого извлекаются аргументы распространения
arrayLength — количество аргументов для распространения из входного массива-аргумента
Возвращает:
новый обработчик метода, который распространяет массив-аргумент в заданной позиции, прежде чем вызвать исходный обработчик метода
Исключения:
NullPointerException — если arrayType является null-ссылкой
IllegalArgumentException — если arrayType не является типом массива, или если цель не имеет как минимум arrayLength типов параметров, или если arrayLength отрицательно, или если spreadArgPos имеет незаконное значение (отрицательное или вместе с arrayLength превышает количество аргументов), или если тип результирующего обработчика метода будет иметь слишком много параметров
WrongMethodTypeException — если неявный asType вызов терпит неудачу
С:
9
См. также:
  • asSpreader(Class, int)

withVarargs

public MethodHandle withVarargs(boolean makeVarargs)
Адаптирует этот обработчик метода к переменному количеству аргументов, если логическое значение истинно, иначе к фиксированному количеству аргументов. Если обработчик метода уже имеет соответствующий режим количества аргументов, он возвращается без изменений.
Примечание API:

Этот метод иногда полезен при адаптации обработчика метода, который может иметь переменное количество аргументов, чтобы убедиться, что результирующий адаптер также имеет переменное количество аргументов тогда и только тогда, когда исходный обработчик имел его. Например, этот код изменяет первый аргумент обработчика mh на int, не нарушая его свойства переменного количества аргументов: mh.asType(mh.type().changeParameterType(0,int.class)) .withVarargs(mh.isVarargsCollector())

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

if (makeVarargs == isVarargsCollector())
  return this;
else if (makeVarargs)
  return asVarargsCollector(type().lastParameterType());
else
  return asFixedArity();
Параметры:
makeVarargs — true, если результирующий обработчик метода должен иметь поведение с переменным количеством аргументов
Возвращает:
обработчик метода того же типа, с возможно изменённым поведением переменного количества аргументов
Исключения:
IllegalArgumentException — если makeVarargs имеет значение true, и у этого обработчика метода нет параметра массива в конце
С:
9
См. также:
  • asVarargsCollector(java.lang.Class<?>)
  • asFixedArity()

asCollector

public MethodHandle asCollector(Class<?> arrayType, int arrayLength)
Создаёт обработчик метода для собирания массива, который принимает заданное количество последующих позиционных аргументов и собирает их в массив-аргумент. Новый обработчик метода адаптирует текущий обработчик метода в качестве цели. Тип адаптера будет таким же, как тип цели, за исключением того, что единственный параметр в конце (обычно типа arrayType) заменяется arrayLength параметрами, тип которых — элемент типа arrayType.

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

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

(Массив также может быть общей константой, когда arrayLength равно нулю.)

(Примечание: arrayType часто идентичен последнему типу параметра исходной цели. Это явный аргумент для симметрии с asSpreader, а также для того, чтобы цель могла использовать простой Object в качестве своего последнего типа параметра.)

Для создания адаптера сбора, который не ограничен определённым количеством собираемых аргументов, используйте asVarargsCollector или withVarargs вместо этого.

Вот несколько примеров обработчиков методов сбора массивов:

MethodHandle deepToString = publicLookup()
  .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
assertEquals("[won]",   (String) deepToString.invokeExact(new Object[]{"won"}));
MethodHandle ts1 = deepToString.asCollector(Object[].class, 1);
assertEquals(methodType(String.class, Object.class), ts1.type());
//assertEquals("[won]", (String) ts1.invokeExact(         new Object[]{"won"})); //FAIL
assertEquals("[[won]]", (String) ts1.invokeExact((Object) new Object[]{"won"}));
// arrayType can be a subtype of Object[]
MethodHandle ts2 = deepToString.asCollector(String[].class, 2);
assertEquals(methodType(String.class, String.class, String.class), ts2.type());
assertEquals("[two, too]", (String) ts2.invokeExact("two", "too"));
MethodHandle ts0 = deepToString.asCollector(Object[].class, 0);
assertEquals("[]", (String) ts0.invokeExact());
// collectors can be nested, Lisp-style
MethodHandle ts22 = deepToString.asCollector(Object[].class, 3).asCollector(String[].class, 2);
assertEquals("[A, B, [C, D]]", ((String) ts22.invokeExact((Object)'A', (Object)"B", "C", "D")));
// arrayType can be any primitive array type
MethodHandle bytesToString = publicLookup()
  .findStatic(Arrays.class, "toString", methodType(String.class, byte[].class))
  .asCollector(byte[].class, 3);
assertEquals("[1, 2, 3]", (String) bytesToString.invokeExact((byte)1, (byte)2, (byte)3));
MethodHandle longsToString = publicLookup()
  .findStatic(Arrays.class, "toString", methodType(String.class, long[].class))
  .asCollector(long[].class, 1);
assertEquals("[123]", (String) longsToString.invokeExact((long)123));

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

Параметры:
arrayType — часто Object[], тип массива-аргумента, который будет собирать аргументы
arrayLength — количество аргументов для сбора в новый массив-аргумент
Возвращает:
новый обработчик метода, который собирает некоторые последующие аргументы в массив, прежде чем вызвать исходный обработчик метода
Исключения:
NullPointerException — если arrayType является null-ссылкой
IllegalArgumentException — если arrayType не является типом массива или arrayType не может быть приведён к типу конечного параметра этого обработчика метода, или arrayLength не является допустимым размером массива, или результирующий обработчик метода имеет слишком много параметров
WrongMethodTypeException — если неявный asType вызов терпит неудачу
См. также:
  • asSpreader(java.lang.Class<?>, int)
  • asVarargsCollector(java.lang.Class<?>)

asCollector

public MethodHandle asCollector(int collectArgPos, Class<?> arrayType, int arrayLength)
Создаёт обработчик метода для собирания массива, который принимает заданное количество позиционных аргументов, начиная с заданной позиции, и собирает их в массив-аргумент. Новый обработчик метода адаптирует текущий обработчик метода в качестве цели. Тип адаптера будет таким же, как тип цели, за исключением того, что параметр в позиции, указанной collectArgPos (обычно типа arrayType), заменяется arrayLength параметрами, тип которых — элемент типа arrayType.

Этот метод очень похож на asCollector(Class, int), но отличается тем, что его collectArgPos аргумент указывает, с какой позиции в списке параметров аргументы должны собираться. Этот индекс начинается с нуля.

Примечание API:
Примеры:
   StringWriter swr = new StringWriter();
   MethodHandle swWrite = LOOKUP.findVirtual(StringWriter.class, "write", methodType(void.class, char[].class, int.class, int.class)).bindTo(swr);
   MethodHandle swWrite4 = swWrite.asCollector(0, char[].class, 4);
   swWrite4.invoke('A', 'B', 'C', 'D', 1, 2);
   assertEquals("BC", swr.toString());
   swWrite4.invoke('P', 'Q', 'R', 'S', 0, 4);
   assertEquals("BCPQRS", swr.toString());
   swWrite4.invoke('W', 'X', 'Y', 'Z', 3, 1);
   assertEquals("BCPQRSZ", swr.toString());

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

Параметры:
collectArgPos — позиция в списке параметров, начиная с которой нужно начать сбор, с индексом 0.
arrayType — часто Object[], тип массива-аргумента, который будет собирать аргументы
arrayLength — количество аргументов для сбора в новый массив-аргумент
Возвращает:
новый обработчик метода, который собирает некоторые аргументы в массив, прежде чем вызвать исходный обработчик метода
Исключения:
NullPointerException — если arrayType является null-ссылкой
IllegalArgumentException — если arrayType не является типом массива или arrayType не может быть приведён к типу параметра массива этого обработчика метода, или arrayLength не является допустимым размером массива, или collectArgPos имеет незаконное значение (отрицательное или больше количества аргументов), или результирующий обработчик метода имеет слишком много параметров
WrongMethodTypeException — если неявный asType вызов терпит неудачу
С:
9
См. также:
  • asCollector(Class, int)

asVarargsCollector

public MethodHandle asVarargsCollector(Class<?> arrayType)
Создаёт адаптер с переменной арностью, способный принимать любое количество позиционных аргументов и собирать их в массив.

Тип и поведение адаптера будут такими же, как у целевого объекта, за исключением того, что определённые invoke и asType запросы могут привести к тому, что последующие позиционные аргументы будут собраны в конечный параметр целевого объекта. Кроме того, тип последнего параметра адаптера будет arrayType, даже если у целевого объекта другой тип последнего параметра.

Данная трансформация может вернуть this, если обработчик метода уже имеет переменную арность, а тип его конечного параметра идентичен arrayType.

При вызове с invokeExact, адаптер вызывает целевой объект без изменения аргументов. (Примечание: Это поведение отличается от коллектора фиксированной арности, так как он принимает весь массив неопределённой длины, а не фиксированное количество аргументов.)

При вызове с обычным, неточной invoke, если тип вызывающего объекта совпадает с типом адаптера, адаптер вызывает целевой объект так же, как с invokeExact. (Это обычное поведение для invoke, когда типы совпадают.)

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

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

Тип вызывающего объекта должен предоставить по меньшей мере достаточное количество аргументов, и правильного типа, чтобы удовлетворить требованию целевого объекта к позиционным аргументам перед конечным аргументом массива. Таким образом, вызывающий объект должен предоставить как минимум N-1 аргументов, где N — арность целевого объекта. Кроме того, должны существовать преобразования из входных аргументов в аргументы целевого объекта. Как и при других применениях обычного invoke, если эти основные требования не выполнены, может быть выброшено WrongMethodTypeException.

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

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

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

Когда обработчик метода получен путём выполнения инструкции ldc с CONSTANT_MethodHandle константой, и целевой метод помечен как метод переменной арности (с модификатором бита 0x0080), обработчик метода будет принимать несколько арностей, как если бы константа обработчика метода была создана с помощью вызова asVarargsCollector.

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

Никакие преобразования обработчиков методов не создают новых обработчиков методов с переменной арностью, если это не указано в документации. Следовательно, помимо asVarargsCollector и withVarargs, все методы в MethodHandle и MethodHandles вернут обработчик метода с фиксированной арностью, за исключением случаев, когда они указаны для возврата своего исходного операнда (например, asType типа самого обработчика метода).

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

Вот пример обработчика метода переменной арности для создания списков:

MethodHandle deepToString = publicLookup()
  .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
MethodHandle ts1 = deepToString.asVarargsCollector(Object[].class);
assertEquals("[won]",   (String) ts1.invokeExact(    new Object[]{"won"}));
assertEquals("[won]",   (String) ts1.invoke(         new Object[]{"won"}));
assertEquals("[won]",   (String) ts1.invoke(                      "won" ));
assertEquals("[[won]]", (String) ts1.invoke((Object) new Object[]{"won"}));
// findStatic of Arrays.asList(...) produces a variable arity method handle:
MethodHandle asList = publicLookup()
  .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class));
assertEquals(methodType(List.class, Object[].class), asList.type());
assert(asList.isVarargsCollector());
assertEquals("[]", asList.invoke().toString());
assertEquals("[1]", asList.invoke(1).toString());
assertEquals("[two, too]", asList.invoke("two", "too").toString());
String[] argv = { "three", "thee", "tee" };
assertEquals("[three, thee, tee]", asList.invoke(argv).toString());
assertEquals("[three, thee, tee]", asList.invoke((Object[])argv).toString());
List ls = (List) asList.invoke((Object)argv);
assertEquals(1, ls.size());
assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));

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

Параметры:
arrayType - часто Object[], тип массива аргументов, который будет собирать аргументы
Возвращает:
новый обработчик метода, который может собрать любое количество последующих аргументов в массив перед вызовом исходного обработчика метода
Исключения:
NullPointerException - если arrayType — null-ссылка
IllegalArgumentException - если arrayType не является типом массива или arrayType не может быть приведён к типу конечного параметра этого обработчика метода
См. также:
  • asCollector(java.lang.Class<?>, int)
  • isVarargsCollector()
  • withVarargs(boolean)
  • asFixedArity()

isVarargsCollector

public boolean isVarargsCollector()
Определяет, поддерживает ли этот обработчик метода вызовы с переменной арностью. Такие обработчики методов возникают из следующих источников:
  • вызов asVarargsCollector
  • вызов метода lookup, который разрешается в метод или конструктор Java с переменной арностью
  • инструкция ldc с константой CONSTANT_MethodHandle, которая разрешается в метод или конструктор Java с переменной арностью
Возвращает:
true, если этот обработчик метода принимает более одной арности обычных, неточных invoke вызовов
См. также:
  • asVarargsCollector(java.lang.Class<?>)
  • asFixedArity()

asFixedArity

public MethodHandle asFixedArity()
Создаёт обработчик метода с фиксированной арностью, который в остальном эквивалентен текущему обработчику метода.

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

В противном случае, полученный обработчик метода с фиксированной арностью имеет тот же тип и поведение, что и текущий обработчик метода, за исключением того, что isVarargsCollector будет false. Обработчик метода с фиксированной арностью может (или не может) быть предыдущим аргументом для asVarargsCollector.

Вот пример обработчика метода переменной арности для создания списков:

MethodHandle asListVar = publicLookup()
  .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
  .asVarargsCollector(Object[].class);
MethodHandle asListFix = asListVar.asFixedArity();
assertEquals("[1]", asListVar.invoke(1).toString());
Exception caught = null;
try { asListFix.invoke((Object)1); }
catch (Exception ex) { caught = ex; }
assert(caught instanceof ClassCastException);
assertEquals("[two, too]", asListVar.invoke("two", "too").toString());
try { asListFix.invoke("two", "too"); }
catch (Exception ex) { caught = ex; }
assert(caught instanceof WrongMethodTypeException);
Object[] argv = { "three", "thee", "tee" };
assertEquals("[three, thee, tee]", asListVar.invoke(argv).toString());
assertEquals("[three, thee, tee]", asListFix.invoke(argv).toString());
assertEquals(1, ((List) asListVar.invoke((Object)argv)).size());
assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
Возвращает:
новый обработчик метода, который принимает только фиксированное количество аргументов
См. также:
  • asVarargsCollector(java.lang.Class<?>)
  • isVarargsCollector()
  • withVarargs(boolean)

bindTo

public MethodHandle bindTo(Object x)
Связывает значение x с первым аргументом обработчика метода без его вызова. Новый обработчик метода адаптирует текущий обработчик метода в качестве цели, связывая его с заданным аргументом. Тип связанного обработчика будет таким же, как и тип цели, за исключением того, что один ведущий параметр ссылки будет опущен.

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

Ссылка x должна быть преобразуема в тип первого параметра цели.

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

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

Parameters:
x - значение, которое необходимо связать с первым аргументом цели
Returns:
новый обработчик метода, который добавляет заданное значение в начало списка входных аргументов перед вызовом исходного обработчика метода
Throws:
IllegalArgumentException - если у цели нет ведущего параметра типа, являющегося типом ссылки
ClassCastException - если x нельзя преобразовать в ведущий тип параметра цели
See Also:
  • MethodHandles.insertArguments(java.lang.invoke.MethodHandle, int, java.lang.Object...)

describeConstable

public Optional<MethodHandleDesc> describeConstable()
Возвращает номинальный дескриптор для этого экземпляра, если его можно создать, или пустой Optional, если это невозможно.
Specified by:
describeConstable in interface Constable
Returns:
An Optional содержащий результирующий номинальный дескриптор, или пустой Optional, если его нельзя создать.
Since:
12

toString

public String toString()
Возвращает строковое представление обработчика метода, начинающееся со строки "MethodHandle" и заканчивающееся строковым представлением типа обработчика метода. Другими словами, этот метод возвращает строку, равную значению:
"MethodHandle" + type().toString()

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

Overrides:
toString in class Object
Returns:
строковое представление обработчика метода

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

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

Spec-Zone.ru

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