Spec-Zone.ru › OpenJDK 25

Класс MethodHandle

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

Содержимое дескриптора метода

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

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

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

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

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

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

Компиляция вызовов дескрипторов методов

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

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

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

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

Вызов дескриптора метода

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

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

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

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

Взаимодействие дескрипторов методов и Core Reflection API

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

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

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

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

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

Взаимодействие дескрипторов методов и обобщений Java

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

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

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

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

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

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

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

Методы, объявленные в классе 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 Core 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 Core 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. Для нулевой ссылки — 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 Core Reflection и JNI. Поэтому его можно использовать как мост между нативным или рефлексивным кодом и дескрипторами методов.

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

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

invokeWithArguments

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

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

  invokeWithArguments(arguments.toArray())

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

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

asType

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

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

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

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

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

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

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

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

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

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

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

asSpreader

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

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

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

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

При вызове адаптера длина переданного аргумента array запрашивается так, как если бы использовался байткод array.length или arraylength. Если адаптер принимает конечный аргумент-массив нулевой длины, переданный аргумент array может быть как массивом нулевой длины, так и null; в противном случае, если массив равен null, адаптер выбросит NullPointerException, а если массив содержит неправильное количество элементов — 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 является нулевой ссылкой
IllegalArgumentException — если arrayType не является типом массива, целевой метод имеет менее arrayLength типов параметров, arrayLength отрицательно или тип полученного дескриптора метода содержал бы слишком много параметров
WrongMethodTypeException — если предполагаемый вызов asType завершается неудачно
См. также:
  • asCollector(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 является нулевой ссылкой
IllegalArgumentException — если arrayType не является типом массива, целевой метод имеет менее arrayLength типов параметров, arrayLength отрицательно, spreadArgPos имеет недопустимое значение (отрицательно либо вместе с длиной массива превышает количество аргументов) или тип полученного дескриптора метода содержал бы слишком много параметров
WrongMethodTypeException — если предполагаемый вызов asType завершается неудачно
Начиная с версии:
9
См. также:
  • asSpreader(Class, int)

withVarargs

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

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

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

if (makeVarargs == isVarargsCollector())
  return this;
else if (makeVarargs)
  return asVarargsCollector(type().lastParameterType());
else
  return asFixedArity();
Параметры:
makeVarargs — true, если возвращаемый дескриптор метода должен принимать переменное число аргументов
Возвращает:
дескриптор метода того же типа, возможно, с изменённым режимом переменного числа аргументов
Выбрасывает:
IllegalArgumentException — если makeVarargs равно true, а у этого дескриптора метода нет конечного параметра-массива
Начиная с версии:
9
См. также:
  • asVarargsCollector(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 является нулевой ссылкой
IllegalArgumentException — если arrayType не является типом массива, arrayType нельзя присвоить типу конечного параметра этого дескриптора метода, arrayLength не является допустимым размером массива либо тип полученного дескриптора метода содержал бы слишком много параметров
WrongMethodTypeException — если предполагаемый вызов asType завершается неудачно
См. также:
  • asSpreader(Class, int)
  • asVarargsCollector(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 является нулевой ссылкой
IllegalArgumentException — если arrayType не является типом массива или arrayType нельзя присвоить типу параметра-массива этого дескриптора метода, либо если arrayLength не является допустимым размером массива, либо если collectArgPos имеет недопустимое значение (отрицательное или превышающее количество аргументов), либо если тип полученного дескриптора метода содержал бы слишком много параметров
WrongMethodTypeException — если предполагаемый вызов asType завершается неудачно
Начиная с версии:
9
См. также:
  • asCollector(Class, int)

asVarargsCollector

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

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

Это преобразование может вернуть this, если дескриптор метода уже принимает переменное число аргументов, а тип его завершающего параметра совпадает с arrayType.

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

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

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

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

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

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

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

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

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

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

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

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

Ниже приведен пример дескриптора метода с переменным числом аргументов, создающего список:

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

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

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

isVarargsCollector

public boolean isVarargsCollector()
Определяет, поддерживает ли этот дескриптор метода вызовы с переменным числом аргументов. Такие дескрипторы методов создаются следующими способами:
  • вызовом asVarargsCollector
  • вызовом метода поиска, который разрешается в метод или конструктор Java с переменным числом аргументов
  • инструкцией ldc для CONSTANT_MethodHandle, которая разрешается в метод или конструктор Java с переменным числом аргументов
Возвращает:
true, если этот дескриптор метода принимает вызовы invoke с обычным неточным вызовом более чем с одной арностью
См. также:
  • asVarargsCollector(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(Class)
  • isVarargsCollector()
  • withVarargs(boolean)

bindTo

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

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

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

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

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

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

describeConstable

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

toString

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

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

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

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

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

Spec-Zone.ru

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