Java 8 ввела методы по умолчанию для интерфейсов. Существующий Реализации InvocationHandler
не будут вызывать методы интерфейса по умолчанию. В этой короткой статье документируются необходимые изменения. Примечание: Сначала в нем описывается реализация, основанная на чтении документов, а затем предоставляется рабочая реализация для Java 8.
Учитывая Обработчик вызовов
реализация:
@Override public Object invoke(Object proxy, Method method, Object[] argv) throws Throwable { Object result = null; /* * Logic to calculate result. */ return result; }
решение Java 8 , по-видимому, заключается в расширении реализации для вызова любых методов интерфейса по умолчанию через MethodHandle
:
@Override public Object invoke(Object proxy, Method method, Object[] argv) throws Throwable { Object result = null; Class> declaringClass = method.getDeclaringClass(); if (method.isDefault()) { result = MethodHandles.lookup() .in(declaringClass) .unreflectSpecial(method, declaringClass) .bindTo(proxy) .invokeWithArguments(argv); } else { /* * Logic to calculate result. */ } return result; }
Если Метод
имеет значение “по умолчанию”, то вызывается метод целевого интерфейса. В противном случае реализация InvocationHandler
выполняется, как и раньше. Любой метод интерфейса по умолчанию должен вызываться:
- Нахождение дескрипторов метода
. Поиск
черезMethodHandles.lookup().in(объявление класса)
, - Получить
MethodHandle
обход любых переопределяющих методов через.unreflectSpecial(метод, объявляющий класс)
и, - Вызовите метод на
прокси-сервере
с помощью.bind To(proxy).invokeWithArguments(argv)
К сожалению, это не работает, если объявляющий класс
не является “приватно доступным” для вызывающего (что происходит большую часть времени), что приводит к:
Caused by: java.lang.IllegalAccessException: no private access for invokespecial: interface package1.SomeInterface, from package1.SomeInterface/public at java.lang.invoke.MemberName.makeAccessException(MemberName.java:850) at java.lang.invoke.MethodHandles$Lookup.checkSpecialCaller(MethodHandles.java:1572) at java.lang.invoke.MethodHandles$Lookup.unreflectSpecial(MethodHandles.java:1231) at package2.InvocationHandlerImpl.invoke(InvocationHandlerImpl.java:59)
Фактическое решение Java 8 является:
@Override public Object invoke(Object proxy, Method method, Object[] argv) throws Throwable { Object result = null; Class> declaringClass = method.getDeclaringClass(); if (method.isDefault()) { Constructorconstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class); constructor.setAccessible(true); result = constructor.newInstance(declaringClass) .in(declaringClass) .unreflectSpecial(method, declaringClass) .bindTo(proxy) .invokeWithArguments(argv); } else { /* * Logic to calculate result. */ } return result; }
Это не будет работать в Java 9 +. В Java 9 и последующих версиях решение должно основываться на дескрипторах методов . Lookup.findSpecial()
и/или Метод Обрабатывает.privateLookupIn()
.
Оригинал: “https://dev.to/allenball/adding-support-to-java-invocationhandler-implementations-for-interface-default-methods-h64”