Spec-Zone.ru › OpenJDK 21

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

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

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

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

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

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

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

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

При первом выполнении инструкции 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 рефлексии

Используя фабричные методы в API Lookup, любой член класса, представленный объектом основного 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

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

Modifier and Type Method Description
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()
Определяет, поддерживает ли данный обработчик метода вызовы с переменной арностью variable arity.
String toString()
Возвращает строковое представление обработчика метода, начинающееся со строки "MethodHandle" и заканчивающееся строковым представлением типа обработчика метода.
MethodType type()
Отчёт о типе этого обработчика метода.
MethodHandle withVarargs(boolean makeVarargs)
Адаптирует этот обработчик метода к вызовам с переменной арностью variable arity, если флаг boolean имеет значение true, иначе к вызовам с фиксированной арностью fixed arity.

Методы, унаследованные от класса 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.
  • Собрать N элементы массива в качестве логического списка аргументов, каждый аргумент статически типизирован как 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)

invokeWithArguments

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

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

  invokeWithArguments(arguments.toArray())

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

Параметры:
arguments - аргументы, которые нужно передать целевому элементу
Возвращает:
результат, возвращённый целевым элементом
Исключения:
NullPointerException - если arguments является нулевой ссылкой
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 — ссылочный тип, в момент выполнения будет применено распаковывающее преобразование, возможно, за которым последует преобразование 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.

Parameters:
newType - ожидаемый тип нового обработчика методов
Returns:
обработчик методов, который делегирует this после выполнения необходимых преобразований аргументов и организует необходимые преобразования возвращаемых значений
Throws:
NullPointerException - если newType является ссылкой null
WrongMethodTypeException - если преобразование невозможно
See Also:
  • 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()));
Parameters:
arrayType - обычно Object[], тип аргумента массива, из которого извлекаются распространяемые аргументы
arrayLength - количество аргументов, которые нужно распространять из входного аргумента массива
Returns:
новый обработчик методов, который распространяет свой конечный аргумент массива перед вызовом исходного обработчика методов
Throws:
NullPointerException - если arrayType является ссылкой null
IllegalArgumentException - если arrayType не является типом массива, или если целевой обработчик не имеет по крайней мере arrayLength типов параметров, или если arrayLength отрицательно, или если тип полученного обработчика методов будет иметь слишком много параметров
WrongMethodTypeException - если неявный вызов asType завершится ошибкой
See Also:
  • asCollector(java.lang.Class<?>, int)

asSpreader

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

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

API Note:
Пример:
   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);
Parameters:
spreadArgPos - позиция (индекс с нуля) в списке аргументов, с которой начинается распространение.
arrayType - обычно Object[], тип аргумента массива, из которого извлекаются распространяемые аргументы
arrayLength - количество аргументов, которые нужно распространять из входного аргумента массива
Returns:
новый обработчик методов, который распространяет аргумент массива в заданной позиции перед вызовом исходного обработчика методов
Throws:
NullPointerException - если arrayType является ссылкой null
IllegalArgumentException - если arrayType не является типом массива, или если целевой обработчик не имеет по крайней мере arrayLength типов параметров, или если arrayLength отрицательно, или если spreadArgPos имеет незаконное значение (отрицательно или в сочетании с arrayLength, превышающей количество аргументов), или если тип полученного обработчика методов будет иметь слишком много параметров
WrongMethodTypeException - если неявный вызов asType завершится ошибкой
Since:
9
See Also:
  • asSpreader(Class, int)

withVarargs

public MethodHandle withVarargs(boolean makeVarargs)
Приспосабливает обработку этого метода к переменному числу аргументов, если флаг boolean равен true, иначе к фиксированному числу аргументов. Если обработчик метода уже имеет нужный режим арности, он возвращается без изменений.
Примечание 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 - нулевая позиция в списке параметров, с которой следует начать сбор.
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 является нулевой ссылкой
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 должна быть преобразуема в тип первого параметра целевого объекта.

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

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

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

describeConstable

public Optional<MethodHandleDesc> describeConstable()
Возвращает номинальный дескриптор для этого экземпляра, если его можно создать, или пустой Optional, если нет.
Указано в:
describeConstable в интерфейсе Constable
Возвращает:
An Optional содержащий полученный номинальный дескриптор, или пустой Optional, если его нельзя создать.
С:
12

toString

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

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

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

© 1993, 2023, 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/21/docs/api/java.base/java/lang/invoke/MethodHandle.html

Spec-Zone.ru

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