Spec-Zone.ru › OpenJDK 17

Интерфейс 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 элементов не проходит преобразование разворачивания, если соответствующий тип параметра метода является примитивным типом; или если, после возможного разворачивания, любой из args элементов не может быть присвоен соответствующему типу параметра метода.
IllegalAccessException - если класс объявления указанного метода по умолчанию недоступен для вызывающего класса
NullPointerException - если proxy или method является null
Throwable - всё, что выброшено методом по умолчанию
См. Спецификацию виртуальной машины Java:
5.4.3. Разрешение метода
С:
16

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

Spec-Zone.ru

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