Класс MethodHandle
- java.lang.Object
-
- java.lang.invoke.MethodHandle
public abstract class MethodHandle extends Object
Обработчик метода — это типизированная, напрямую исполняемая ссылка на базовый метод, конструктор, поле или подобную низкоуровневую операцию, с опциональными преобразованиями аргументов или возвращаемых значений. Эти преобразования достаточно общие и включают такие паттерны, как преобразование, вставку, удаление и замену.
Содержание обработчика метода
Обработчики методов динамически и строго типизированы в соответствии с типами параметров и возвращаемых значений. Они не различаются по имени или определяющему классу своих базовых методов. Обработчик метода должен вызываться с помощью символического описателя типа, который соответствует собственному описателю типа обработчика метода. Каждый обработчик метода сообщает свой описатель типа через type аксессор. Этот описатель типа — это объект MethodType, структура которого представляет собой серию классов, один из которых является типом возвращаемого значения метода (или void.class если такового нет).
Тип обработчика метода управляет типами вызовов, которые он принимает, и видами преобразований, которые к нему применяются.
Обработчик метода содержит пару специальных методов вызова, называемых invokeExact и invoke. Оба метода вызова обеспечивают прямой доступ к базовому методу, конструктору, полю или другой операции обработчика метода, как изменённые преобразованиями аргументов и возвращаемых значений. Оба вызывающих метода принимают вызовы, которые точно соответствуют типу самого обработчика метода. Обычный, неточный вызывающий метод также принимает ряд других типов вызовов.
Обработчики методов неизменяемы и не имеют видимого состояния. Конечно, они могут быть связаны с базовыми методами или данными, которые демонстрируют состояние. С точки зрения модели памяти Java, любой обработчик метода будет вести себя так, как будто все его (внутренние) поля являются переменными final. Это означает, что любой обработчик метода, ставший видимым для приложения, всегда будет полностью сформирован. Это справедливо даже если обработчик метода опубликован через общую переменную в гонке данных.
Обработчики методов не могут быть подклассифицированы пользователем. Реализации могут (или не могут) создавать внутренние подклассы MethodHandle, которые могут быть видны через операцию Object.getClass. Программист не должен делать выводы о обработчике метода по его конкретному классу, так как иерархия классов обработчика метода (если она есть) может время от времени или в реализациях разных поставщиков изменяться.
Компиляция обработчиков методов
Выражение вызова Java метода, называяinvokeExact или invoke, может вызывать обработчик метода из исходного кода Java. С точки зрения исходного кода, эти методы могут принимать любые аргументы, и их результат может быть приведён к любому типу возвращаемого значения. Формально это достигается путём предоставления методам вызова Object типов возвращаемых значений и аргументов с переменной арностью Object, но у них есть дополнительное свойство, называемое полиморфизмом сигнатуры, которое связывает эту свободу вызова непосредственно со стеком выполнения JVM. Как обычно с виртуальными методами, вызовы на уровне исходного кода к invokeExact и invoke компилируются в инструкцию invokevirtual. Более необычно, компилятор должен записывать фактические типы аргументов и не может выполнять преобразования вызова метода над аргументами. Вместо этого он должен генерировать инструкции, которые помещают их в стек в соответствии с их собственными не преобразованными типами. Объект обработчика метода сам помещается в стек перед аргументами. Затем компилятор генерирует инструкцию invokevirtual, которая вызывает обработчик метода с символическим описателем типа, описывающим типы аргументов и возвращаемого значения.
Для выдачи полного символического описателя типа компилятор также должен определить тип возвращаемого значения. Это основано на приведении к типу в выражении вызова метода, если оно есть, или Object, если вызов — выражение, или void, если вызов — оператор. Приведение к типу может быть к примитивному типу (но не void).
Как особый случай, неприведённому аргументу null даётся символический описатель типа java.lang.Void. Неясность с типом Void безвредна, так как нет ссылок типа Void кроме ссылки null.
Вызов обработчика метода
В первый раз, когда выполняется инструкцияinvokevirtual, она связывается путём символического разрешения имён в инструкции и проверки того, что вызов метода статически законен. Это также верно для вызовов invokeExact и invoke. В этом случае символический описатель типа, выданный компилятором, проверяется на корректность синтаксиса, а имена, которые он содержит, разрешаются. Таким образом, инструкция invokevirtual, которая вызывает обработчик метода, всегда будет связываться, если символический описатель типа синтаксически корректен и типы существуют. Когда инструкция invokevirtual выполняется после связывания, JVM сначала проверяет тип получаемого обработчика метода, чтобы убедиться, что он соответствует символическому описателю типа. Если проверка типа не пройдена, это означает, что метод, который вызывается вызывающей стороной, не присутствует в отдельном обработчике метода, к которому обращаются.
В случае invokeExact, описатель типа вызова (после разрешения символических имён типа) должен точно соответствовать типу метода получаемого обработчика метода. В случае обычного, неточного invoke, разрешённый описатель типа должен быть допустимым аргументом метода получателя asType. Таким образом, обычный invoke более допускает, чем invokeExact.
После проверки типа вызов invokeExact напрямую и немедленно вызывает базовый метод обработчика метода (или другое поведение, как в случае).
Вызов обычного invoke работает так же, как вызов invokeExact, если символический описатель типа, указанный вызывающей стороной, точно соответствует типу обработчика метода. Если есть несоответствие типов, invoke пытается скорректировать тип получаемого обработчика метода, как будто путём вызова asType, чтобы получить обработчик метода, который можно вызвать точно M2. Это позволяет более гибко согласовывать тип метода между вызывающей и вызываемой сторонами.
(Примечание: скорректированный обработчик метода M2 не является напрямую наблюдаемым, и поэтому реализации не обязаны его материализовывать.)
Проверка вызова
В типичных программах проверка типа обработчика метода, как правило, проходит успешно. Но если совпадение не найдено, JVM сгенерирует исключениеWrongMethodTypeException, либо напрямую (в случае invokeExact), либо косвенно, как при неудачном вызове asType (в случае invoke). Таким образом, несоответствие типов метода, которое могло бы проявиться как ошибка связывания в статически типизированной программе, может проявиться как динамическая ошибка WrongMethodTypeException в программе, использующей обработчики методов.
Поскольку типы методов содержат «живые» Class объекты, проверка соответствия типов метода учитывает как имена типов, так и загрузчики классов. Таким образом, даже если обработчик метода M создан в одном загрузчике классов L1 и используется в другом L2, вызовы обработчика метода являются типобезопасными, потому что символический описатель типа вызывающей стороны, как разрешённый в L2, сопоставляется с исходным символическим описателем типа метода вызываемого объекта, как разрешённый в L1. Разрешение в L1 происходит при создании M и присвоении его типу, а разрешение в L2 происходит при связывании инструкции invokevirtual.
Помимо проверки описателя типа, возможности обработчика метода вызывать его базовый метод не ограничены. Если обработчик метода сформирован на непубличном методе классом, имеющим доступ к этому методу, полученный обработчик можно использовать в любом месте любым вызывающим объектом, который получает ссылку на него.
В отличие от API Core Reflection, где проверка доступа выполняется каждый раз при вызове рефлексивного метода, проверка доступа к обработчику метода выполняется при создании обработчика метода. В случае ldc (см. ниже) проверка доступа выполняется в рамках связывания записи константного пула, лежащей в основе константного обработчика метода.
Таким образом, обработчики непубличных методов или методов непубличных классов, как правило, должны храниться конфиденциально. Их не следует передавать небезопасным кодам, если их использование небезопасным кодом не будет безобидным.
Создание обработчика метода
Исходный код Java может создать обработчик метода, который напрямую обращается к любому методу, конструктору или полю, к которому у этого кода есть доступ. Это делается с помощью рефлексивного, основанного на возможностях API, называемогоMethodHandles.Lookup. Например, статический обработчик метода можно получить из Lookup.findStatic. Также есть методы преобразования из объектов Core Reflection API, такие как Lookup.unreflect. Как классы и строки, обработчики методов, соответствующие доступным полям, методам и конструкторам, также могут быть представлены непосредственно в константном пуле файла класса как константы, которые будут загружаться байт-кодами ldc. Новый тип записи константного пула, CONSTANT_MethodHandle, напрямую ссылается на связанную запись константного пула CONSTANT_Methodref, CONSTANT_InterfaceMethodref или CONSTANT_Fieldref. (Подробности о константах обработчика метода см. в разделах 4.4.8 и 5.4.3.5 спецификации Java Virtual Machine.)
Обработчики методов, полученные из просмотров или загрузки констант из методов или конструкторов с битом модификатора переменной арности (0x0080), имеют соответствующую переменную арность, как если бы они были определены с помощью asVarargsCollector или withVarargs.
Ссылка на метод может ссылаться либо на статический, либо на нестатический метод. В случае нестатического метода тип обработчика метода включает явный аргумент получателя, предваряющий все остальные аргументы. В типе обработчика метода начальный аргумент получателя типизируется в соответствии с классом, в котором метод изначально был запрошен. (Например, если обработчик нестатического метода получен через ldc, тип получателя — это класс, указанный в записи константного пула.)
Константы обработчика методов подвержены тем же проверкам доступа во время связывания, что и соответствующие инструкции байт-кода, и инструкция ldc будет генерировать соответствующие ошибки связывания, если поведение байт-кода сгенерировало бы такие ошибки.
В качестве следствия из этого, доступ к защищенным членам ограничен только получателями класса, который осуществляет доступ, или одного из его подклассов, а сам класс доступа должен, в свою очередь, являться подклассом (или пакетом-близнецом) определяющего класса защищенного члена. Если ссылка на метод относится к защищенному нестатическому методу или полю класса, находящегося вне текущего пакета, аргумент receiver будет сужен до типа класса доступа.
При вызове обработчика метода виртуального метода метод всегда ищется в получателе (то есть в первом аргументе).
Также можно создать обработчик невиртуального метода для конкретной реализации виртуального метода. Они не выполняют виртуальный поиск на основе типа получателя. Такой обработчик метода имитирует эффект инструкции 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
- См. также:
-
MethodType,MethodHandles
Методы
| Модификатор и тип | Метод | Описание |
|---|---|---|
MethodHandle | asCollector(int collectArgPos,
Class<?> arrayType,
int arrayLength) | Создает обработку метода, собирающего массив, которая принимает заданное количество позиционных аргументов, начиная с заданной позиции, и собирает их в массив аргументов. |
MethodHandle | asCollector(Class<?> arrayType,
int arrayLength) | Создает обработку метода, собирающего массив, которая принимает заданное количество конечных позиционных аргументов и собирает их в массив аргументов. |
MethodHandle | asFixedArity() | Создает обработку метода с фиксированной арностью, которая в противном случае эквивалентна текущей обработке метода. |
MethodHandle | asSpreader(int spreadArgPos,
Class<?> arrayType,
int arrayLength) | Создает обработку метода, распространяющего массив, которая принимает массив аргументов в заданной позиции и распространяет его элементы как позиционные аргументы вместо массива. |
MethodHandle | asSpreader(Class<?> arrayType,
int arrayLength) | Создает обработку метода, распространяющего массив, которая принимает конечный массив аргументов и распространяет его элементы как позиционные аргументы. |
MethodHandle | asType(MethodType newType) | Создает обработку адаптера метода, которая адаптирует тип текущей обработки метода к новому типу. |
MethodHandle | asVarargsCollector(Class<?> arrayType) | Создает адаптер с переменной арностью, который может принимать любое количество конечных позиционных аргументов и собирать их в массив аргументов. |
MethodHandle | bindTo(Object x) | Связывает значение |
Object | invoke(Object... args) | Вызывает обработку метода, позволяя использовать любой описатель типа вызывающего объекта и необязательно выполняя преобразования над аргументами и возвращаемыми значениями. |
Object | invokeExact(Object... args) | Вызывает обработку метода, позволяя использовать любой описатель типа вызывающего объекта, но требуя точного соответствия типа. |
Object | invokeWithArguments(Object... arguments) | Выполняет вызов с переменной арностью, передавая аргументы из заданного массива в обработку метода, как если бы это было через неточный |
Object | invokeWithArguments(List<?> arguments) | Выполняет вызов с переменной арностью, передавая аргументы из заданного списка в обработку метода, как если бы это было через неточный |
boolean | isVarargsCollector() | Определяет, поддерживает ли эта обработка метода вызовы с переменной арностью. |
String | toString() | Возвращает строковое представление обработки метода, начинающееся со строки |
MethodType | type() | Сообщает о типе этой обработки метода. |
MethodHandle | withVarargs(boolean makeVarargs) | Адаптирует эту обработку метода для использования с переменной арностью, если булево значение истинно, в противном случае с фиксированной арностью. |
Методы, унаследованные от класса java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait Методы
type
public MethodType type()
Сообщает о типе этого обработчика методов. Каждое вызов этого обработчика методов через invokeExact должно точно соответствовать этому типу.
- Возвращает:
- тип обработчика методов
invokeExact
public final Object invokeExact(Object... args)
throws Throwable Вызывает обработчик методов, разрешая любой тип вызывающей стороны, но требуя точного соответствия типа. Символьный тип описания в месте вызова invokeExact должен точно соответствовать типу этого обработчика методов type. Преобразования аргументов или возвращаемых значений не допускаются.
При наблюдении этого метода через API ядра отражения он будет отображаться как один метод нативной функции, принимающий массив объектов и возвращающий объект. Если этот нативный метод вызывается непосредственно через java.lang.reflect.Method.invoke, через JNI или косвенно через Lookup.unreflect, он будет выбрасывать UnsupportedOperationException.
- Параметры:
-
args- список параметров с полиморфизмом подписи, статически представленный с помощью varargs - Возвращает:
- результат с полиморфизмом подписи, статически представленный с помощью
Object - Исключения:
-
WrongMethodTypeException- если тип целевого объекта не идентичен символьному описанию типа вызывающей стороны -
Throwable- все исключения, выброшенные базовым методом, передаются без изменений через вызов обработчика методов
invoke
public final Object invoke(Object... args)
throws Throwable Вызывает обработчик методов, разрешая любой тип вызывающей стороны и необязательно выполняя преобразования аргументов и возвращаемых значений.
Если символьное описание типа места вызова точно соответствует типу этого обработчика методов type, вызов выполняется так, как если бы он был выполнен с помощью invokeExact.
В противном случае вызов выполняется так, как если бы этот обработчик методов сначала был изменён вызовом asType для изменения этого обработчика методов до требуемого типа, а затем вызов выполняется так, как если бы он был выполнен с помощью invokeExact на изменённом обработчике методов.
Нет гарантии, что вызов asType действительно будет сделан. Если JVM может предсказать результаты вызова, она может выполнить адаптации непосредственно над аргументами вызывающей стороны и вызвать целевой обработчик методов в соответствии с его точным типом.
Тип описания, разрешённый в месте вызова invoke , должен быть допустимым аргументом для метода целевого объекта asType. В частности, вызывающая сторона должна указать тот же арность аргументов, что и тип вызываемого объекта, если вызываемый объект не является коллектором переменной арности.
При наблюдении этого метода через API ядра отражения он будет отображаться как один метод нативной функции, принимающий массив объектов и возвращающий объект. Если этот нативный метод вызывается непосредственно через java.lang.reflect.Method.invoke, через JNI или косвенно через Lookup.unreflect, он будет выбрасывать UnsupportedOperationException.
- Параметры:
-
args- список параметров с полиморфизмом подписи, статически представленный с помощью varargs - Возвращает:
- результат с полиморфизмом подписи, статически представленный с помощью
Object - Исключения:
-
WrongMethodTypeException- если тип целевого объекта не может быть изменён до символьного описания типа вызывающей стороны -
ClassCastException- если тип целевого объекта может быть изменён до вызывающей стороны, но преобразование ссылки не удаётся -
Throwable- все исключения, выброшенные базовым методом, передаются без изменений через вызов обработчика методов
invokeWithArguments
public Object invokeWithArguments(Object... arguments)
throws Throwable Выполняет вызов с переменной арностью, передавая аргументы в заданном массиве обработчику методов, как если бы это был неточный invoke из места вызова, который упоминает только тип Object, а фактическое количество аргументов равно длине массива аргументов.
В частности, выполнение происходит по следующим этапам, хотя методы не гарантируются для вызова, если JVM может предсказать их эффекты.
- Определение длины массива аргументов как
N. Для 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 ядра отражения и JNI. Это, следовательно, может быть использовано как мост между кодом нативного или рефлективного кода и обработчиками методов.
- Примечание API:
- Этот вызов примерно эквивалентен следующему коду:
// for jumbo argument lists, adapt varargs explicitly: int N = (arguments == null? 0: arguments.length); int M = this.type.parameterCount(); int MAX_SAFE = 127; // 127 longs require 254 slots, which is OK if (N > MAX_SAFE && N > M && this.isVarargsCollector()) { Class<?> arrayType = this.type().lastParameterType(); Class<?> elemType = arrayType.getComponentType(); if (elemType != null) { Object args2 = Array.newInstance(elemType, M); MethodHandle arraySetter = MethodHandles.arrayElementSetter(arrayType); for (int i = 0; i < M; i++) { arraySetter.invoke(args2, i, arguments[M-1 + i]); } arguments = Arrays.copyOf(arguments, M); arguments[M-1] = args2; return this.asFixedArity().invokeWithArguments(arguments); } } // done with explicit varargs processing // Handle fixed arity and non-jumbo variable arity invocation. MethodHandle invoker = MethodHandles.spreadInvoker(this.type(), 0); Object result = invoker.invokeExact(this, arguments); - Параметры:
-
arguments- аргументы для передачи целевому объекту - Возвращает:
- результат, возвращённый целевым объектом
- Исключения:
-
ClassCastException- если аргумент не может быть преобразован с помощью преобразования ссылок -
WrongMethodTypeException- если тип целевого объекта не может быть изменён для принятия указанного количестваObjectаргументов -
Throwable- любые исключения, выброшенные вызовом целевого метода - См. также:
MethodHandles.spreadInvoker(java.lang.invoke.MethodType, int)
invokeWithArguments
public Object invokeWithArguments(List<?> arguments)
throws Throwable Выполняет вызов с переменной арностью, передавая аргументы в заданном списке обработчику методов, как если бы это был неточный invoke из места вызова, который упоминает только тип Object, а фактическое количество аргументов равно длине списка аргументов.
Этот метод также эквивалентен следующему коду:
invokeWithArguments(arguments.toArray())
Списки большого размера приемлемы, если этот обработчик методов имеет переменную арность. Подробности см. в invokeWithArguments(Object[]).
- Параметры:
-
arguments- аргументы для передачи целевому объекту - Возвращает:
- результат, возвращённый целевым объектом
- Исключения:
-
NullPointerException- еслиargumentsявляется null ссылкой -
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 - примитивным типом, то в runtime будет применено преобразование распаковки, возможно, за которым следует преобразование вызова Java-метода (JLS 5.3) над примитивным значением. (Это расширяющие преобразования примитивных типов). T0 должен быть классом-обёрткой или супертипом одного из них. (В случае, когда T0 - Object, это те преобразования, которые разрешены
java.lang.reflect.Method.invoke.) Преобразование распаковки должно иметь возможность успешного выполнения, что означает, что если T0 сам по себе не является классом-обёрткой, должен существовать хотя бы один класс-обёртка TW, который является подтипом T0, и значение которого можно расширить до T1. - Если тип возвращаемого значения T1 помечен как void, любое возвращаемое значение игнорируется.
- Если тип возвращаемого значения T0 равен void, а T1 - ссылка, вводится значение null.
- Если тип возвращаемого значения T0 равен void, а T1 - примитивный тип, вводится нулевое значение.
Преобразование обработчика метода невозможно, если любое из необходимых парных преобразований невозможно.
Во время выполнения, преобразования, применяемые к ссылочным аргументам или возвращаемым значениям, могут потребовать дополнительных проверок во время выполнения, которые могут завершиться неудачей. Операция распаковки может завершиться неудачей, потому что исходная ссылка равна null, вызывая NullPointerException. Операция распаковки или приведение ссылочного типа могут также завершиться неудачей при ссылке на объект неверного типа, вызывая ClassCastException. Хотя операция распаковки может принимать несколько типов обёртки, если ни одна из них не доступна, будет брошено ClassCastException.
- Параметры:
-
newType- ожидаемый тип нового обработчика метода - Возвращает:
- обработчик метода, который делегирует
thisпосле выполнения необходимых преобразований аргументов и обеспечивает необходимые преобразования возвращаемого значения - Исключения:
-
NullPointerException- еслиnewTypeявляется ссылкой на null -
WrongMethodTypeException- если преобразование невозможно - См. также:
MethodHandles.explicitCastArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)
asSpreader
public MethodHandle asSpreader(Class<?> arrayType,
int arrayLength) Создаёт обработчик метода для распределения массива, принимающий аргумент-массив в конце и распределяющий его элементы как позиционные аргументы. Новый обработчик метода адаптирует текущий обработчик метода как свой целевой обработчик. Тип адаптера будет таким же, как тип целевого, за исключением того, что последние arrayLength параметры типа целевого обработчика будут заменены единственным параметром-массивом типа arrayType.
Если тип элемента массива отличается от любого из соответствующих типов аргументов в исходном целевом обработчике, исходный целевой обработчик адаптируется для непосредственного приёма элементов массива, как если бы был вызван asType.
При вызове адаптер заменяет аргумент-массив в конце элементами массива, каждый как свой собственный аргумент целевому обработчику. (Порядок аргументов сохраняется). Они преобразуются попарно путём приведения типов и/или распаковки к типам хвостовых параметров целевого обработчика. Наконец, вызывается целевой обработчик. То, что возвращает целевой обработчик, возвращается адаптером без изменений.
Перед вызовом целевого обработчика адаптер проверяет, что массив содержит ровно достаточно элементов для обеспечения правильного количества аргументов для целевого обработчика метода. (Массив также может быть null, когда требуется ноль элементов.)
При вызове адаптера длина предоставленного array аргумента запрашивается как если бы это делалось с помощью array.length или arraylength байткода. Если адаптер принимает аргумент-массив нулевой длины, предоставленный array аргумент может быть либо массивом нулевой длины, либо null; в противном случае адаптер выбросит NullPointerException, если массив null, и выбросит IllegalArgumentException, если массив не имеет правильного количества элементов.
Вот несколько простых примеров обработчиков методов распределения массивов:
MethodHandle equals = publicLookup()
.findVirtual(String.class, "equals", methodType(boolean.class, Object.class));
assert( (boolean) equals.invokeExact("me", (Object)"me"));
assert(!(boolean) equals.invokeExact("me", (Object)"thee"));
// spread both arguments from a 2-array:
MethodHandle eq2 = equals.asSpreader(Object[].class, 2);
assert( (boolean) eq2.invokeExact(new Object[]{ "me", "me" }));
assert(!(boolean) eq2.invokeExact(new Object[]{ "me", "thee" }));
// try to spread from anything but a 2-array:
for (int n = 0; n <= 10; n++) {
Object[] badArityArgs = (n == 2 ? new Object[0] : new Object[n]);
try { assert((boolean) eq2.invokeExact(badArityArgs) && false); }
catch (IllegalArgumentException ex) { } // OK
}
// spread both arguments from a String array:
MethodHandle eq2s = equals.asSpreader(String[].class, 2);
assert( (boolean) eq2s.invokeExact(new String[]{ "me", "me" }));
assert(!(boolean) eq2s.invokeExact(new String[]{ "me", "thee" }));
// spread second arguments from a 1-array:
MethodHandle eq1 = equals.asSpreader(Object[].class, 1);
assert( (boolean) eq1.invokeExact("me", new Object[]{ "me" }));
assert(!(boolean) eq1.invokeExact("me", new Object[]{ "thee" }));
// spread no arguments from a 0-array or null:
MethodHandle eq0 = equals.asSpreader(Object[].class, 0);
assert( (boolean) eq0.invokeExact("me", (Object)"me", new Object[0]));
assert(!(boolean) eq0.invokeExact("me", (Object)"thee", (Object[])null));
// asSpreader and asCollector are approximate inverses:
for (int n = 0; n <= 2; n++) {
for (Class<?> a : new Class<?>[]{Object[].class, String[].class, CharSequence[].class}) {
MethodHandle equals2 = equals.asSpreader(a, n).asCollector(a, n);
assert( (boolean) equals2.invokeWithArguments("me", "me"));
assert(!(boolean) equals2.invokeWithArguments("me", "thee"));
}
}
MethodHandle caToString = publicLookup()
.findStatic(Arrays.class, "toString", methodType(String.class, char[].class));
assertEquals("[A, B, C]", (String) caToString.invokeExact("ABC".toCharArray()));
MethodHandle caString3 = caToString.asCollector(char[].class, 3);
assertEquals("[A, B, C]", (String) caString3.invokeExact('A', 'B', 'C'));
MethodHandle caToString2 = caString3.asSpreader(char[].class, 2);
assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray()));
- Параметры:
-
arrayType- обычноObject[], тип аргумента-массива, из которого необходимо извлечь распределяемые аргументы -
arrayLength- количество аргументов для распределения из входного аргумента-массива - Возвращает:
- новый обработчик метода, который распределяет свой конечный аргумент-массив перед вызовом исходного обработчика метода
- Исключения:
-
NullPointerException- еслиarrayTypeявляется ссылкой на null -
IllegalArgumentException- еслиarrayTypeне является типом массива или если целевой обработчик не имеет как минимумarrayLengthтипов параметров или еслиarrayLengthотрицательно или если результирующий тип обработчика метода имел бы слишком много параметров -
WrongMethodTypeException- если неявный вызовasTypeзавершается неудачей - См. также:
asCollector(java.lang.Class<?>, int)
asSpreader
public MethodHandle asSpreader(int spreadArgPos,
Class<?> arrayType,
int arrayLength) Создаёт обработчик метода для распределения массива, принимающий аргумент-массив в заданной позиции и распределяющий его элементы как позиционные аргументы вместо массива. Новый обработчик метода адаптирует текущий обработчик метода как свой целевой обработчик. Тип адаптера будет таким же, как тип целевого, за исключением того, что arrayLength параметры типа целевого обработчика, начиная с нулевой позиции spreadArgPos, заменяются единственным параметром-массивом типа arrayType.
Этот метод ведёт себя очень похоже на asSpreader(Class, int), но принимает дополнительный spreadArgPos аргумент, чтобы указать, в какой позиции в списке параметров должно произойти распределение.
- Примечание API:
- Пример:
MethodHandle compare = LOOKUP.findStatic(Objects.class, "compare", methodType(int.class, Object.class, Object.class, Comparator.class)); MethodHandle compare2FromArray = compare.asSpreader(0, Object[].class, 2); Object[] ints = new Object[]{3, 9, 7, 7}; Comparator<Integer> cmp = (a, b) -> a - b; assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 0, 2), cmp) < 0); assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 1, 3), cmp) > 0); assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 2, 4), cmp) == 0); - Параметры:
-
spreadArgPos- позиция (индекс, начинающийся с нуля) в списке аргументов, с которой должно начаться распределение. -
arrayType- обычноObject[], тип аргумента-массива, из которого необходимо извлечь распределяемые аргументы -
arrayLength- количество аргументов для распределения из входного аргумента-массива - Возвращает:
- новый обработчик метода, который распределяет аргумент-массив в заданной позиции перед вызовом исходного обработчика метода
- Исключения:
-
NullPointerException- еслиarrayTypeявляется ссылкой на null -
IllegalArgumentException- еслиarrayTypeне является типом массива или если целевой обработчик не имеет как минимумarrayLengthтипов параметров или еслиarrayLengthотрицательно или еслиspreadArgPosимеет незаконное значение (отрицательное или вместе с длиной массива, превышающей количество аргументов) или если результирующий тип обработчика метода имел бы слишком много параметров -
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 return asFixedArity();
- Параметры:
-
makeVarargs- true, если обработчик возвращаемого метода должен иметь поведение с переменным количеством аргументов - Возвращает:
- обработчик метода того же типа, с возможно изменённым поведением переменного количества аргументов
- Издаёт исключение:
-
IllegalArgumentException- еслиmakeVarargsравно true, а этот обработчик метода не имеет параметра в виде массива - С момента:
- 9
- См. также:
-
asVarargsCollector(java.lang.Class<?>),asFixedArity()
asCollector
public MethodHandle asCollector(Class<?> arrayType,
int arrayLength) Создаёт обработчик метода для сбора массива, который принимает заданное количество хвостовых позиционных аргументов и собирает их в массив аргументов. Новый обработчик метода адаптирует текущий обработчик метода как свой целевой обработчик. Тип адаптера будет таким же, как тип целевого обработчика, за исключением того, что один хвостовой параметр (обычно типа arrayType) заменяется на arrayLength параметров, тип которых соответствует типу элемента arrayType.
Если тип массива отличается от типа конечного аргумента в исходном целевом обработчике, исходный целевой обработчик адаптируется для работы непосредственно с типом массива, как если бы был вызван asType.
При вызове адаптер заменяет свои хвостовые arrayLength аргументы одним новым массивом типа arrayType, элементы которого (в порядке следования) составляют заменённые аргументы. Затем вызывается целевой обработчик. Возвращаемое целевым обработчиком значение возвращается адаптером без изменений.
(Массив также может быть общим постоянным значением, когда arrayLength равно нулю.)
(Примечание: Тип arrayType часто идентичен типу последнего параметра исходного целевого обработчика. Это явный аргумент для симметрии с asSpreader, а также для того, чтобы целевой обработчик мог использовать простой Object в качестве типа последнего параметра.)
Для создания адаптера сбора, который не ограничен определённым количеством собираемых аргументов, используйте asVarargsCollector или withVarargs вместо этого.
Вот некоторые примеры обработчиков методов сбора массивов:
MethodHandle deepToString = publicLookup()
.findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
assertEquals("[won]", (String) deepToString.invokeExact(new Object[]{"won"}));
MethodHandle ts1 = deepToString.asCollector(Object[].class, 1);
assertEquals(methodType(String.class, Object.class), ts1.type());
//assertEquals("[won]", (String) ts1.invokeExact( new Object[]{"won"})); //FAIL
assertEquals("[[won]]", (String) ts1.invokeExact((Object) new Object[]{"won"}));
// arrayType can be a subtype of Object[]
MethodHandle ts2 = deepToString.asCollector(String[].class, 2);
assertEquals(methodType(String.class, String.class, String.class), ts2.type());
assertEquals("[two, too]", (String) ts2.invokeExact("two", "too"));
MethodHandle ts0 = deepToString.asCollector(Object[].class, 0);
assertEquals("[]", (String) ts0.invokeExact());
// collectors can be nested, Lisp-style
MethodHandle ts22 = deepToString.asCollector(Object[].class, 3).asCollector(String[].class, 2);
assertEquals("[A, B, [C, D]]", ((String) ts22.invokeExact((Object)'A', (Object)"B", "C", "D")));
// arrayType can be any primitive array type
MethodHandle bytesToString = publicLookup()
.findStatic(Arrays.class, "toString", methodType(String.class, byte[].class))
.asCollector(byte[].class, 3);
assertEquals("[1, 2, 3]", (String) bytesToString.invokeExact((byte)1, (byte)2, (byte)3));
MethodHandle longsToString = publicLookup()
.findStatic(Arrays.class, "toString", methodType(String.class, long[].class))
.asCollector(long[].class, 1);
assertEquals("[123]", (String) longsToString.invokeExact((long)123)); Примечание: Результирующий адаптер никогда не является обработчиком метода с переменным числом аргументов, даже если исходный целевой обработчик метода был таким.
- Параметры:
-
arrayType- частоObject[], тип аргумента массива, который будет собирать аргументы -
arrayLength- количество аргументов для сбора в новый массив аргументов - Возвращает:
- новый обработчик метода, который собирает некоторые хвостовые аргументы в массив перед вызовом исходного обработчика метода
- Издаёт исключение:
-
NullPointerException- еслиarrayTypeявляется ссылкой на null -
IllegalArgumentException- еслиarrayTypeне является типом массива илиarrayTypeне может быть присвоено типу хвостового параметра этого обработчика метода, илиarrayLengthне является допустимым размером массива, или тип результирующего обработчика метода имел бы слишком много параметров -
WrongMethodTypeException- если неявный вызовasTypeзавершился ошибкой - См. также:
-
asSpreader(java.lang.Class<?>, int),asVarargsCollector(java.lang.Class<?>)
asCollector
public MethodHandle asCollector(int collectArgPos,
Class<?> arrayType,
int arrayLength) Создаёт обработчик метода для сбора массива, который принимает заданное число позиционных аргументов, начиная с заданной позиции, и собирает их в аргумент массива. Новый обработчик метода адаптирует текущий обработчик метода как свой целевой обработчик. Тип адаптера будет таким же, как тип целевого обработчика, за исключением того, что параметр в позиции, указанной collectArgPos (обычно типа arrayType), заменяется на arrayLength параметров, тип которых соответствует типу элемента arrayType.
Этот метод работает очень похоже на asCollector(Class, int), но отличается тем, что его аргумент
collectArgPos указывает, в какой позиции списка параметров следует начинать сбор аргументов. Этот индекс нулевой.
- Примечание API:
- Примеры:
StringWriter swr = new StringWriter(); MethodHandle swWrite = LOOKUP.findVirtual(StringWriter.class, "write", methodType(void.class, char[].class, int.class, int.class)).bindTo(swr); MethodHandle swWrite4 = swWrite.asCollector(0, char[].class, 4); swWrite4.invoke('A', 'B', 'C', 'D', 1, 2); assertEquals("BC", swr.toString()); swWrite4.invoke('P', 'Q', 'R', 'S', 0, 4); assertEquals("BCPQRS", swr.toString()); swWrite4.invoke('W', 'X', 'Y', 'Z', 3, 1); assertEquals("BCPQRSZ", swr.toString());Примечание: Результирующий адаптер никогда не является обработчиком метода с переменным числом аргументов, даже если исходный целевой обработчик метода был таким.
- Параметры:
-
collectArgPos- нулевая позиция в списке параметров, с которой следует начать сбор. -
arrayType- частоObject[], тип аргумента массива, который будет собирать аргументы -
arrayLength- количество аргументов для сбора в новый массив аргументов - Возвращает:
- новый обработчик метода, который собирает некоторые аргументы в массив перед вызовом исходного обработчика метода
- Издаёт исключение:
-
NullPointerException- еслиarrayTypeявляется ссылкой на null -
IllegalArgumentException- еслиarrayTypeне является типом массива илиarrayTypeне может быть присвоено типу параметра-массива этого обработчика метода, илиarrayLengthне является допустимым размером массива, илиcollectArgPosимеет недопустимое значение (отрицательное или большее, чем количество аргументов), или тип результирующего обработчика метода имел бы слишком много параметров -
WrongMethodTypeException- если неявный вызовasTypeзавершился ошибкой - С момента:
- 9
- См. также:
asCollector(Class, int)
asVarargsCollector
public MethodHandle asVarargsCollector(Class<?> arrayType)
Создаёт адаптер с переменной арностью, способный принимать любое количество позиционных аргументов в конце и собирать их в массив-аргумент.
Тип и поведение адаптера будут такими же, как тип и поведение целевого объекта, за исключением того, что определённые invoke и asType запросы могут привести к тому, что позиционные аргументы в конце будут собраны в заключительный параметр целевого объекта. Также, тип последнего параметра адаптера будет arrayType, даже если у целевого объекта другой тип последнего параметра.
Эта трансформация может вернуть this если обработчик метода уже имеет переменную арность и его тип заключительного параметра идентичен arrayType.
При вызове с invokeExact, адаптер вызывает целевой объект без изменений аргументов. (Примечание: Это поведение отличается от поведения коллектора с фиксированной арностью, поскольку он принимает целый массив неопределённой длины, а не фиксированное количество аргументов.)
При вызове с обычным неточным invoke, если тип вызывающего объекта совпадает с типом адаптера, адаптер вызывает целевой объект так же, как с invokeExact. (Это обычное поведение для invoke при совпадении типов.)
В противном случае, если арность вызывающего объекта и адаптера одинаковы, и тип заключительного параметра вызывающего объекта является ссылочным типом, идентичным или приводимым к типу заключительного параметра адаптера, аргументы и возвращаемые значения преобразуются попарно, как если бы это выполнялось с помощью asType для обработчика метода с фиксированной арностью.
В противном случае, арности различаются или тип заключительного параметра адаптера не приводим к соответствующему типу вызывающего объекта. В этом случае адаптер заменяет все аргументы с конца от исходного положения аргумента вплоть до конца новым массивом типа arrayType, элементы которого представляют собой (в порядке) заменённые аргументы.
Тип вызывающего объекта должен предоставлять как минимум достаточно аргументов и правильного типа, чтобы удовлетворить требования целевого объекта к позиционным аргументам перед заключительным массивом-аргументом. Таким образом, вызывающий объект должен предоставить как минимум N-1 аргументов, где N — арность целевого объекта. Также должны существовать преобразования от входящих аргументов к аргументам целевого объекта. Как и при других операциях с обычным invoke, если эти основные требования не выполняются, может быть выброшено исключение WrongMethodTypeException.
Во всех случаях, то, что в итоге возвращает целевой объект, возвращается адаптером без изменений.
В последнем случае это точно так же, как если бы обработчик целевого метода временно адаптировался с помощью коллектора с фиксированной арностью до арности, требуемой типом вызывающего объекта. (Как и с asCollector, если длина массива равна нулю, может использоваться общая константа вместо нового массива. Если предполагаемый вызов asCollector бросил бы исключение IllegalArgumentException или WrongMethodTypeException, вызов адаптера переменной арности должен бросить WrongMethodTypeException.)
Поведение asType также специализировано для адаптеров переменной арности, чтобы сохранить инвариант, что обычный неточный invoke всегда эквивалентен вызову asType для корректировки типа целевого объекта, за которым следует invokeExact. Поэтому адаптер переменной арности реагирует на запрос asType путем создания коллектора с фиксированной арностью, если и только если адаптер и запрашиваемый тип отличаются либо по арности, либо по типу заключительного аргумента. Результирующий коллектор с фиксированной арностью дополнительно корректирует свой тип (при необходимости) до запрашиваемого типа путём попарного преобразования, как если бы это выполнялось с помощью ещё одного применения asType.
Когда обработчик метода получается путём выполнения инструкции ldc константы CONSTANT_MethodHandle, и целевой метод помечен как метод с переменной арностью (с модификатором 0x0080 ), обработчик метода будет принимать множество арностей, как если бы константа обработчика метода была создана путём вызова asVarargsCollector.
Для создания адаптера сбора, который собирает предопределённое количество аргументов, и тип которого отражает это предопределённое число, используйте asCollector вместо этого.
Ни одна трансформация обработчика методов не создаёт новые обработчики методов с переменной арностью, если это не указано в документации. Поэтому, помимо asVarargsCollector и withVarargs, все методы в MethodHandle и MethodHandles вернут обработчик метода с фиксированной арностью, за исключением случаев, когда они указаны для возвращения своего исходного операнда (например, asType типа обработчика метода).
Вызов asVarargsCollector для обработчика метода, который уже имеет переменную арность, создаст обработчик метода с тем же типом и поведением. Он может (или не может) вернуть исходный обработчик метода с переменной арностью.
Вот пример обработчика метода с переменной арностью для создания списков:
MethodHandle deepToString = publicLookup()
.findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
MethodHandle ts1 = deepToString.asVarargsCollector(Object[].class);
assertEquals("[won]", (String) ts1.invokeExact( new Object[]{"won"}));
assertEquals("[won]", (String) ts1.invoke( new Object[]{"won"}));
assertEquals("[won]", (String) ts1.invoke( "won" ));
assertEquals("[[won]]", (String) ts1.invoke((Object) new Object[]{"won"}));
// findStatic of Arrays.asList(...) produces a variable arity method handle:
MethodHandle asList = publicLookup()
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class));
assertEquals(methodType(List.class, Object[].class), asList.type());
assert(asList.isVarargsCollector());
assertEquals("[]", asList.invoke().toString());
assertEquals("[1]", asList.invoke(1).toString());
assertEquals("[two, too]", asList.invoke("two", "too").toString());
String[] argv = { "three", "thee", "tee" };
assertEquals("[three, thee, tee]", asList.invoke(argv).toString());
assertEquals("[three, thee, tee]", asList.invoke((Object[])argv).toString());
List ls = (List) asList.invoke((Object)argv);
assertEquals(1, ls.size());
assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0))); Обсуждение: Эти правила разработаны как динамически типизированная разновидность правил Java для методов с переменной арностью. В обоих случаях, вызывающие объекты метода или обработчика метода с переменной арностью могут передавать ноль или более позиционных аргументов, или же передавать предварительно собранные массивы любой длины. Пользователи должны быть осведомлены о специальной роли последнего аргумента и о влиянии совпадения типов на этот заключительный аргумент, что определяет, интерпретируется ли единственный заключительный аргумент как целый массив или как одиночный элемент массива, который должен быть собран. Обратите внимание, что динамический тип заключительного аргумента не влияет на это решение, только сравнение символьного описателя типа места вызова и описателя типа обработчика метода.)
- Параметры:
-
arrayType- частоObject[], тип массива-аргумента, который будет собирать аргументы - Возвращает:
- новый обработчик метода, который может собрать любое количество аргументов в конец в массив, прежде чем вызвать исходный обработчик метода
- Бросает:
-
NullPointerException- еслиarrayType— ссылка null -
IllegalArgumentException- еслиarrayType— не массивный тип илиarrayTypeне приводим к типу заключительного параметра этого обработчика метода - См. также:
-
asCollector(java.lang.Class<?>, int),isVarargsCollector(),withVarargs(boolean),asFixedArity()
isVarargsCollector
public boolean isVarargsCollector()
Определяет, поддерживает ли этот обработчик метода вызовы с переменной арностью. Такие обработчики методов возникают из следующих источников:
- вызов asVarargsCollector
- вызов метода lookup, который разрешает переменную арность Java-метода или конструктора
- инструкция
ldcконстантыCONSTANT_MethodHandle, которая разрешает переменную арность Java-метода или конструктора
- Возвращает:
- true, если этот обработчик метода принимает более одной арности вызовов с обычным неточным
invoke - См. также:
-
asVarargsCollector(java.lang.Class<?>),asFixedArity()
asFixedArity
public MethodHandle asFixedArity()
Создаёт обработчик метода с фиксированной арностью, который в остальном эквивалентен текущему обработчику метода.
Если текущий обработчик метода не имеет переменной арности, возвращается текущий обработчик метода. Это верно даже если текущий обработчик метода не мог быть допустимым входом для asVarargsCollector.
В противном случае, результирующий обработчик метода с фиксированной арностью имеет тот же тип и поведение, что и текущий обработчик метода, за исключением того, что isVarargsCollector будет false. Обработчик метода с фиксированной арностью может (или не может) быть предыдущим аргументом для asVarargsCollector.
Вот пример обработчика метода с переменной арностью для создания списков:
MethodHandle asListVar = publicLookup()
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
.asVarargsCollector(Object[].class);
MethodHandle asListFix = asListVar.asFixedArity();
assertEquals("[1]", asListVar.invoke(1).toString());
Exception caught = null;
try { asListFix.invoke((Object)1); }
catch (Exception ex) { caught = ex; }
assert(caught instanceof ClassCastException);
assertEquals("[two, too]", asListVar.invoke("two", "too").toString());
try { asListFix.invoke("two", "too"); }
catch (Exception ex) { caught = ex; }
assert(caught instanceof WrongMethodTypeException);
Object[] argv = { "three", "thee", "tee" };
assertEquals("[three, thee, tee]", asListVar.invoke(argv).toString());
assertEquals("[three, thee, tee]", asListFix.invoke(argv).toString());
assertEquals(1, ((List) asListVar.invoke((Object)argv)).size());
assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
- Возвращает:
- новый обработчик метода, который принимает только фиксированное количество аргументов
- См. также:
-
asVarargsCollector(java.lang.Class<?>),isVarargsCollector(),withVarargs(boolean)
bindTo
public MethodHandle bindTo(Object x)
Связывает значение x с первым аргументом обработчика метода без его вызова. Новый обработчик метода адаптирует текущий обработчик метода, связывая его с данным аргументом, как свой целевой объект. Тип связанного обработчика будет таким же, как тип целевого объекта, за исключением того, что единственный ведущий ссылочный параметр будет опущен.
При вызове связанный обработчик вставляет данное значение x в качестве нового ведущего аргумента целевому объекту. Другие аргументы также передаются без изменений. То, что целевой объект возвращает в конечном итоге, возвращается связанным обработчиком без изменений.
Ссылка x должна быть преобразуема в тип первого параметра целевого объекта.
Примечание: Поскольку обработчики методов неизменяемы, целевой обработчик метода сохраняет свой исходный тип и поведение.
Примечание: Результирующий адаптер никогда не является обработчиком метода с переменной арностью, даже если исходный целевой обработчик метода был таковым.
- Параметры:
-
x- значение, которое нужно связать с первым аргументом целевого объекта - Возвращает:
- новый обработчик метода, который добавляет данное значение в начало списка входящих аргументов перед вызовом исходного обработчика метода
- Бросает:
-
IllegalArgumentException- если целевой объект не имеет ведущего параметра типа ссылочного типа -
ClassCastException- еслиxне может быть преобразовано в ведущий параметр целевого объекта - См. также:
MethodHandles.insertArguments(java.lang.invoke.MethodHandle, int, java.lang.Object...)
toString
public String toString()
Возвращает строковое представление обработчика метода, начинающееся со строки "MethodHandle" и заканчивающееся строковым представлением типа обработчика метода. Другими словами, этот метод возвращает строку, равную значению:
"MethodHandle" + type().toString()
(Примечание: Будущие релизы этой API могут добавить дополнительную информацию в строковое представление. Поэтому текущий синтаксис не должен анализироваться приложениями.)
© 1993, 2020, 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/11/docs/api/java.base/java/lang/invoke/MethodHandle.html