Spec-Zone.ru › OpenJDK 21

Интерфейс InvocationHandler

Все известные реализующие классы:
CompositeDataInvocationHandler, EventHandler, MBeanServerInvocationHandler, RemoteObjectInvocationHandler
public interface InvocationHandler
InvocationHandler — это интерфейс, реализуемый обработчиком вызова экземпляра прокси.

Каждый экземпляр прокси имеет связанный с ним обработчик вызова. Когда метод вызывается на экземпляре прокси, вызов метода кодируется и передаётся обработчику вызова в метод invoke.

С тех пор:
1.3
См. также:
  • Proxy

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

Модификатор и тип Метод Описание
Object invoke(Object proxy, Method method, Object[] args)
Обрабатывает вызов метода на экземпляре прокси и возвращает результат.
static Object invokeDefault(Object proxy, Method method, Object... args)
Вызывает указанный метод по умолчанию на заданном экземпляре proxy с заданными параметрами.

Подробное описание методов

invoke

Object invoke(Object proxy, Method method, Object[] args) throws Throwable
Обрабатывает вызов метода на экземпляре прокси и возвращает результат. Этот метод будет вызван обработчиком вызова, когда метод вызывается на экземпляре прокси, с которым он связан.
Параметры:
proxy - экземпляр прокси, на котором был вызван метод
method - экземпляр Method, соответствующий методу интерфейса, вызванному на экземпляре прокси. Класс объявления объекта Method будет интерфейсом, в котором метод был объявлен, что может быть суперинтерфейсом интерфейса прокси, который класс прокси наследует через метод.
args - массив объектов, содержащий значения аргументов, переданных в вызов метода на экземпляре прокси, или null, если метод интерфейса не принимает аргументы. Аргументы примитивных типов обернуты в экземпляры соответствующего класса обертки примитивного типа, например java.lang.Integer или java.lang.Boolean.
Возвращает:
значение, возвращаемое из вызова метода на экземпляре прокси. Если объявленный тип возвращаемого значения метода интерфейса — примитивный тип, то возвращаемое значение должно быть экземпляром соответствующего класса обертки примитивного типа; в противном случае — тип, присваиваемый объявленному типу возвращаемого значения. Если возвращаемое значение метода равно null и тип возвращаемого значения метода интерфейса — примитивный, то вызов метода на экземпляре прокси сгенерирует NullPointerException. Если возвращаемое значение метода несовместимо с объявленным типом возвращаемого значения метода интерфейса, как описано выше, вызов метода на экземпляре прокси сгенерирует ClassCastException.
Исключение:
Throwable - исключение, которое необходимо выбросить при вызове метода на экземпляре прокси. Тип исключения должен быть присваиваемым либо любому из типов исключений, объявленных в разделе throws метода интерфейса, либо к необрабатываемым типам исключений java.lang.RuntimeException или java.lang.Error. Если метод сгенерирует проверяемое исключение, которое не может быть присвоено ни одному из типов исключений, объявленных в throws разделе метода интерфейса, то вызов метода на экземпляре прокси сгенерирует UndeclaredThrowableException, содержащий исключение, сгенерированное этим методом.
См. также:
  • UndeclaredThrowableException

invokeDefault

static Object invokeDefault(Object proxy, Method method, Object... args) throws Throwable
Вызывает указанный метод по умолчанию на заданном экземпляре proxy с заданными параметрами. Заданный method должен быть методом по умолчанию, объявленным в интерфейсе прокси класса proxy или унаследованным от его суперинтерфейсов напрямую или косвенно.

Вызов этого метода выполняется так, как если бы инструкция invokespecial выполнялась из класса прокси, нацеливаясь на метод по умолчанию в интерфейсе прокси. Это эквивалентно вызову: X.super.m(A* a) , где X — интерфейс прокси, а вызов X.super::m(A*) решен для данного method.

Примеры: интерфейс A и B оба объявляют реализацию по умолчанию метода m. Интерфейс C расширяет A и наследует метод по умолчанию m от своего суперинтерфейса A.


 interface A {
     default T m(A a) { return t1; }
 }
 interface B {
     default T m(A a) { return t2; }
 }
 interface C extends A {}
 
Следующий пример создаёт экземпляр прокси, который реализует A и вызывает метод по умолчанию A::m.

 Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { A.class },
         (o, m, params) -> {
             if (m.isDefault()) {
                 // if it's a default method, invoke it
                 return InvocationHandler.invokeDefault(o, m, params);
             }
         });
 
Если экземпляр прокси реализует как A, так и B, оба из которых обеспечивают реализацию по умолчанию метода m, обработчик вызова может передать вызов метода A::m или B::m через метод invokeDefault. Например, следующий код делегирует вызов метода B::m.

 Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { A.class, B.class },
         (o, m, params) -> {
             if (m.getName().equals("m")) {
                 // invoke B::m instead of A::m
                 Method bMethod = B.class.getMethod(m.getName(), m.getParameterTypes());
                 return InvocationHandler.invokeDefault(o, bMethod, params);
             }
         });
 
Если экземпляр прокси реализует C, который наследует метод по умолчанию m от своего суперинтерфейса A, тогда вызов метода интерфейса на "m" передаётся обработчику вызова в метод invoke с аргументом объекта Method, представляющим метод по умолчанию A::m.

 Object proxy = Proxy.newProxyInstance(loader, new Class<?>[] { C.class },
        (o, m, params) -> {
             if (m.isDefault()) {
                 // behaves as if calling C.super.m(params)
                 return InvocationHandler.invokeDefault(o, m, params);
             }
        });
 
Вызов метода "m" в этом proxy будет работать так, как если бы вызывался C.super::m и был решен в вызов A::m.

Добавление метода по умолчанию или изменение метода с абстрактного на метод по умолчанию может вызвать исключение, если существующий код пытается вызвать invokeDefault для вызова метода по умолчанию. Например, если C изменяется для реализации метода по умолчанию m:


 interface C extends A {
     default T m(A a) { return t3; }
 }
 
Приведённый выше код, создающий экземпляр прокси proxy с изменённым C, выполняется без исключений, и он приведёт к вызову C::m вместо A::m.

Вот ещё один пример, который создаёт экземпляр прокси C и обработчик вызовов вызывает метод invokeDefault для вызова A::m:


 C c = (C) Proxy.newProxyInstance(loader, new Class<?>[] { C.class },
         (o, m, params) -> {
             if (m.getName().equals("m")) {
                 // IllegalArgumentException thrown as {@code A::m} is not a method
                 // inherited from its proxy interface C
                 Method aMethod = A.class.getMethod(m.getName(), m.getParameterTypes());
                 return InvocationHandler.invokeDefault(o, aMethod params);
             }
         });
 c.m(...);
 
Приведенный выше код успешно выполняется со старой версией C и вызывается A::m. При выполнении с новой версией C вышеприведённый код завершится с IllegalArgumentException, потому что C переопределяет реализацию того же метода и A::m недоступно для экземпляра прокси.
Примечание API:
Параметр proxy имеет тип Object вместо Proxy для упрощения реализации InvocationHandler::invoke для прямого вызова без необходимости преобразования типов.
Параметры:
proxy - экземпляр Proxy, на котором будет вызван метод по умолчанию
method - экземпляр Method, соответствующий методу по умолчанию, объявленному в интерфейсе прокси класса или унаследованному от его суперинтерфейсов напрямую или косвенно
args - параметры, используемые для вызова метода; может быть null, если метод не требует формальных параметров.
Возвращает:
значение, возвращаемое из вызова метода
Исключение:
IllegalArgumentException - если какое-либо из следующих условий выполняется true:
  • proxy не является экземпляром прокси; или
  • указанный method не является методом по умолчанию, объявленным в интерфейсе прокси класса и не унаследованным ни от одного из его суперинтерфейсов; или
  • указанный method переопределён напрямую или косвенно интерфейсами прокси, и ссылка на метод с указанным именем никогда не разрешается на указанный method; или
  • длина массива args не соответствует числу параметров вызываемого метода; или
  • любой из args элементов не проходит преобразование в unboxing, если соответствующий тип параметра метода — примитивный тип; или после unboxing любой из args элементов не может быть присвоен соответствующему типу параметра метода.
IllegalAccessException - если класс объявления указанного метода по умолчанию недоступен для вызывающего класса
NullPointerException - если proxy или method является null
Throwable - всё, что сгенерирует метод по умолчанию
См. Спецификацию виртуальной машины Java:
5.4.3. Разрешение метода
С тех пор:
16

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

Spec-Zone.ru

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