Класс 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
Используя фабричные методы в APILookup, любой член класса, представленный объектом 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(или другой метод полиморфный по сигнатуре) не является виртуальным, он потребляет дополнительный аргумент для самого обработчика метода, в дополнение к любому объекту невиртуального получателя.
IllegalArgumentException. В частности, тип обработчика метода не должен иметь арность, равную точному максимуму 255.- С момента:
- 1.7
- См. также:
Краткое описание методов
| Modifier and Type | Method | Описание |
|---|---|---|
MethodHandle |
asCollector |
Создаёт обработчик метода, собирающего массив, который принимает заданное количество позиционных аргументов, начиная с заданной позиции, и собирает их в массив аргументов. |
MethodHandle |
asCollector |
Создаёт обработчик метода, собирающего массив, который принимает заданное количество заключительных позиционных аргументов и собирает их в массив аргументов. |
MethodHandle |
asFixedArity() |
Создаёт обработчик метода с фиксированной арностью, который в противном случае эквивалентен текущему обработчику метода. |
MethodHandle |
asSpreader |
Создаёт обработчик метода, распространяющего массив, который принимает массив аргументов в заданной позиции и распространяет его элементы как позиционные аргументы вместо массива. |
MethodHandle |
asSpreader |
Создаёт обработчик метода, распространяющего массив, который принимает заключительный массив аргументов и распространяет его элементы как позиционные аргументы. |
MethodHandle |
asType |
Создаёт адаптер обработчика метода, который адаптирует тип текущего обработчика метода к новому типу. |
MethodHandle |
asVarargsCollector |
Создаёт адаптер переменной арности, который может принимать любое количество заключительных позиционных аргументов и собирать их в массив аргументов. |
MethodHandle |
bindTo |
Связывает значение x с первым аргументом метода, не вызывая его. |
Optional<MethodHandleDesc> |
describeConstable() |
Возвращает номинальный дескриптор для этого экземпляра, если он может быть построен, или пустой Optional, если он не может быть построен. |
final Object |
invoke |
Вызывает обработчик метода, позволяя любому дескриптору типа вызывающей стороны и необязательно выполняя преобразования над аргументами и возвращаемыми значениями. |
final Object |
invokeExact |
Вызывает обработчик метода, позволяя любому дескриптору типа вызывающей стороны, но требуя точного соответствия типов. |
Object |
invokeWithArguments |
Выполняет вызов переменной арности, передавая аргументы в заданном массиве обработчику метода, как если бы это был неточный invoke из места вызова, которое упоминает только тип Object, а фактическое количество аргументов — длина массива аргументов. |
Object |
invokeWithArguments |
Выполняет вызов переменной арности, передавая аргументы в заданном списке обработчику метода, как если бы это был неточный invoke из места вызова, которое упоминает только тип Object, а фактическое количество аргументов — длина списка аргументов. |
boolean |
isVarargsCollector() |
Определяет, поддерживает ли этот обработчик метода вызовы переменной арности переменной арности. |
String |
toString() |
Возвращает строковое представление обработчика метода, начиная со строки "MethodHandle" и заканчивая строковым представлением типа обработчика метода. |
MethodType |
type() |
Сообщает о типе этого обработчика метода. |
MethodHandle |
withVarargs |
Адаптирует этот обработчик метода, чтобы он был переменной арности, если флаг boolean равен true, иначе фиксированной арности. |
Подробное описание методов
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- любые исключения, выброшенные вызовом целевого метода - См. также:
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 — примитивный тип, вводится нулевое значение.
Преобразование обработчика метода невозможно, если одно из необходимых попарных преобразований невозможно.
Во время выполнения преобразования, применяемые к ссылкам на аргументы или возвращаемые значения, могут потребовать дополнительные проверки во время выполнения, которые могут завершиться ошибкой. Операция распаковки может завершиться ошибкой, потому что исходная ссылка равна null, что вызовет NullPointerException. Операция распаковки или приведение типов ссылок также может завершиться ошибкой при ссылке на объект неправильного типа, что вызовет ClassCastException. Хотя операция распаковки может принимать несколько видов оболочек, если ни одна из них не доступна, будет выброшено ClassCastException.
- Parameters:
-
newType- ожидаемый тип нового обработчика метода - Returns:
- обработчик метода, который делегирует
thisпосле выполнения необходимых преобразований аргументов и организует необходимые преобразования возвращаемых значений - Throws:
-
NullPointerException- еслиnewTypeявляется нулевым значением -
WrongMethodTypeException- если преобразование невозможно - See Also:
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:
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
- См. также:
withVarargs
public MethodHandle withVarargs(boolean makeVarargs)
- Замечание API:
-
Этот метод иногда полезен при адаптации обработчика метода, который может иметь переменную арность, для обеспечения того, что результирующий адаптер также имеет переменную арность только тогда, когда исходный обработчик её имел. Например, этот код меняет первый аргумент обработчика
mhнаintбез нарушения свойства переменной арности:mh.asType(mh.type().changeParameterType(0,int.class)) .withVarargs(mh.isVarargsCollector())Этот вызов приблизительно эквивалентен следующему коду:
if (makeVarargs == isVarargsCollector()) return this; else if (makeVarargs) return asVarargsCollector(type().lastParameterType()); else return asFixedArity(); - Параметры:
-
makeVarargs- true, если результирующий обработчик метода должен иметь поведение переменной арности - Возвращает:
- обработчик метода того же типа, с, возможно, изменённым поведением переменной арности
- Исключения:
-
IllegalArgumentException- еслиmakeVarargsравно true, а у этого обработчика метода нет параметра массива в конце - С:
- 9
- См. также:
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завершается неудачно - См. также:
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
- См. также:
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:
isVarargsCollector
public boolean isVarargsCollector()
- вызов asVarargsCollector
- вызов метода lookup, который разрешается до Java-метода или конструктора с переменным числом аргументов
- инструкция
ldcконстантыCONSTANT_MethodHandle, которая разрешается до Java-метода или конструктора с переменным числом аргументов
- Returns:
- true, если этот обработчик метода принимает более одного числа аргументов для обычных, неточных вызовов
invoke - See Also:
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:
bindTo
public MethodHandle bindTo(Object x)
x с первым аргументом обработчика метода без его вызова. Новый обработчик метода адаптирует текущий обработчик метода, связывая его с заданным аргументом, как целевой. Тип связанного обработчика будет таким же, как тип целевого, за исключением того, что один ведущий параметр ссылки будет опущен. При вызове связанный обработчик вставляет заданное значение x в качестве нового ведущего аргумента целевому. Другие аргументы также передаются без изменений. То, что в конечном итоге возвращает целевой обработчик, возвращается связанным обработчиком без изменений.
Ссылка x должна быть преобразуема в тип первого параметра целевого обработчика.
Примечание: Поскольку обработчики методов неизменяемы, целевой обработчик метода сохраняет свой исходный тип и поведение.
Примечание: Результирующий адаптер никогда не является обработчиком метода с переменным числом аргументов, даже если исходный целевой обработчик метода был таким.
- Parameters:
-
x- значение для связи с первым аргументом целевого обработчика - Returns:
- новый обработчик метода, который добавляет заданное значение в список входящих аргументов перед вызовом исходного обработчика метода
- Throws:
-
IllegalArgumentException- если у целевого обработчика нет ведущего параметра типа, являющегося типом ссылки -
ClassCastException- еслиxне может быть преобразован в ведущий тип параметра целевого обработчика - See Also:
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