Spec-Zone.ru › OpenJDK 17

Класс MethodHandle

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

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

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

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

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

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

Обработчики методов неизменяемы и не имеют видимого состояния. Конечно, они могут быть связаны с базовыми функциями или данными, которые демонстрируют состояние. С точки зрения Java Memory Model, любой обработчик метода будет вести себя так, как будто все его (внутренние) поля являются переменными типа 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. Также есть методы преобразования из объектов Core Reflection API, такие как 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 Core Reflection

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

В качестве специального случая, при использовании API Core Reflection для просмотра полиморфных по сигнатуре методов 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 Core Reflection, тип обработчика метода будет построен из стирания типов исходного уровня. При вызове обработчика метода типы его аргументов или тип возвращаемого значения могут быть дженериками или экземплярами типов. Если это происходит, компилятор заменит эти типы их стираниями при построении символического описателя типа для инструкции invokevirtual.

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

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

Пределы арности

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

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

Modifier and Type Method Описание
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)
Создаёт обработчик метода, распространяющего массив, который принимает заключительный массив аргументов и распространяет его элементы как позиционные аргументы.
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 ядра Reflection, он будет отображаться как один метод натив, принимающий массив объектов и возвращающий объект. Если этот метод натив вызывается непосредственно через 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 ядра Reflection, он будет отображаться как один метод натив, принимающий массив объектов и возвращающий объект. Если этот метод натив вызывается непосредственно через 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. Для ссылки null, 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 ядра Reflection и 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[]).

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

asType

public 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 является нулевым значением
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 имеет нулевое значение
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:
Пример:

    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 имеет незаконное значение (отрицательное или вместе с длиной массива превышающее количество аргументов), или если тип результирующего обработчика метода имел бы слишком много параметров
WrongMethodTypeException - если неявный вызов asType завершается неудачно
С:
9
См. также:
  • 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 для методов с переменным числом аргументов. В обоих случаях вызывающие методы или обработчики методов с переменным числом аргументов могут передавать ноль или более позиционных аргументов, или же передавать предварительно собранные массивы любой длины. Пользователи должны быть осведомлены о специальной роли конечного аргумента и о влиянии совпадения типов на этот конечный аргумент, что определяет, интерпретируется ли единственный хвостовой аргумент как целый массив или как единственный элемент массива для сбора. Обратите внимание, что динамический тип хвостового аргумента не влияет на это решение, а только сравнение символического описания типа места вызова и описания типа обработчика метода.)

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

isVarargsCollector

public boolean isVarargsCollector()
Определяет, поддерживает ли этот обработчик метода вызовы с переменным числом аргументов. Такие обработчики методов возникают из следующих источников:
  • вызов asVarargsCollector
  • вызов метода lookup, который разрешается до Java-метода или конструктора с переменным числом аргументов
  • инструкция ldc константы CONSTANT_MethodHandle, которая разрешается до Java-метода или конструктора с переменным числом аргументов
Returns:
true, если этот обработчик метода принимает более одного числа аргументов для обычных, неточных вызовов invoke
See Also:
  • 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());
 
Returns:
новый обработчик метода, который принимает только фиксированное число аргументов
See Also:
  • 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 в интерфейсе Constable
Returns:
An Optional содержащий результирующий номинальный дескриптор или пустой Optional, если он не может быть создан.
Since:
12

toString

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

 "MethodHandle" + type().toString()
 

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

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

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

Spec-Zone.ru

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