如何调用Java接口中默认方法?

server/2024/10/19 7:31:40/

从JDK8开始,接口支持默认方法实现,即在接口中可以有具体的实现,仅需使用关键字 default修饰方法即可,如:

java">public interface MyInterface {default void call(String methodName) {System.out.println("MethodHandles, call method: " + methodName);}
}

那么,如果该接口我们不想有实现类,又想要调用 MyInterface#call 方法怎么办?

此时,可借助JDK给我们提供的方法句柄实现:java.lang.invoke.MethodHandles,从而可动态调用方法。

对于方法句柄的调用实现,不同JDK版本可略有差异,简单说明:

jdk8中如果直接调用{@link MethodHandles#lookup()}获取到的{@link MethodHandles.Lookup}
在调用方法 {@link MethodHandles.Lookup#findSpecial(java.lang.Class, java.lang.String, java.lang.invoke.MethodType, java.lang.Class)}
和{@link MethodHandles.Lookup#unreflectSpecial(java.lang.reflect.Method, java.lang.Class)}
获取父类方法句柄{@link MethodHandle}时
可能出现权限不够, 抛出如下异常, 所以通过反射创建{@link MethodHandles.Lookup}解决该问题.
java.lang.IllegalAccessException: no private access for invokespecial: interface com.example.demo.methodhandle.UserService, from com.example.demo.methodhandle.UserServiceInvoke
at java.lang.invoke.MemberName.makeAccessException(MemberName.java:850)
at java.lang.invoke.MethodHandles$Lookup.checkSpecialCaller(MethodHandles.java:1572)
而jdk11中直接调用{@link MethodHandles#lookup()}获取到的{@link MethodHandles.Lookup},也只能对接口类型才会权限获取方法的方法句柄{@link MethodHandle}.
如果是普通类型Class,需要使用jdk9开始提供的 MethodHandles#privateLookupIn(java.lang.Class, java.lang.invoke.MethodHandles.Lookup)方法.

注意:接口没有实现,不能直接通过反射调用!!

以下是针对不同版本编写的一个工具类

java">import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public final class MethodHandlesUtil {private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;private static Constructor<MethodHandles.Lookup> java8LookupConstructor;private static Method privateLookupInMethod;static {//jdk9开始提供的java.lang.invoke.MethodHandles.privateLookupIn方法try {privateLookupInMethod = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);} catch (NoSuchMethodException e) {privateLookupInMethod = null;logger.info("There is no [java.lang.invoke.MethodHandles.privateLookupIn(Class, Lookup)] method in this version of JDK");}//jdk8其实也适用于jdk9及以上的版本,但是上面优先,可以避免 jdk9 反射警告if (privateLookupInMethod == null) {try {java8LookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);java8LookupConstructor.setAccessible(true);} catch (NoSuchMethodException e) {//可能是jdk8 以下版本throw new IllegalStateException("There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.",e);}}}/*** java9中的MethodHandles.lookup()方法返回的Lookup对象* 有权限访问specialCaller != lookupClass()的类* 但是只能适用于接口, {@link java.lang.invoke.MethodHandles.Lookup#checkSpecialCaller}*/public static MethodHandles.Lookup lookup(Class<?> callerClass) {//使用反射,因为当前jdk可能不是java9或以上版本if (privateLookupInMethod != null) {try {return (MethodHandles.Lookup) privateLookupInMethod.invoke(MethodHandles.class, callerClass, MethodHandles.lookup());} catch (IllegalAccessException | InvocationTargetException e) {throw new RuntimeException(e);}}//jdk8try {return java8LookupConstructor.newInstance(callerClass, ALLOWED_MODES);} catch (Exception e) {throw new IllegalStateException("no 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.", e);}}public static MethodHandle getSpecialMethodHandle(Method parentMethod) {final Class<?> declaringClass = parentMethod.getDeclaringClass();MethodHandles.Lookup lookup = lookup(declaringClass);try {return lookup.unreflectSpecial(parentMethod, declaringClass);} catch (IllegalAccessException e) {throw new RuntimeException(e);}}
}

简单使用,执行 MyInterface#call方法调用

利用JDK动态代理创建一个代理类

java">public static void main(String[] args) {// 创建JDK代理类MyInterface myInterface = (MyInterface) Proxy.newProxyInstance(MyInterface.class.getClassLoader(),new Class[]{MyInterface.class},(proxy, method, arg) -> MethodHandlesUtil.getSpecialMethodHandle(method).bindTo(proxy).invokeWithArguments(arg));// 调用call方法myInterface.call("MyInterface#call");
}

执行结果

可能有人就要问了,为什么不能直接调用 method.invoke(proxy, arg)?

原因很简单,JDK动态代理接口的对象本身就是java.lang.reflect.InvocationHandler,如果再调用method.invoke(proxy, arg),那么就会陷入无限循环中。因此,只能采取其他方式,比如以上获取的是接口的默认实现方法;当然,如果是接口的抽象方法,那么就可以在代理方法java.lang.reflect.InvocationHandler#invoke实现自己的逻辑,比如调用外部接口的处理逻辑等等。

最后,感谢大佬,借鉴:java8中实现调用对象的父类方法_privatelookupin-CSDN博客


http://www.ppmy.cn/server/38263.html

相关文章

3、Qt--配置文件的使用

开发平台&#xff1a;Win10 64位 开发环境&#xff1a;Qt Creator 13.0.0 构建环境&#xff1a;Qt 5.15.2 MSVC2019 64位 一、需求及方案 实际开发过程中&#xff0c;我们需要根据本地的配置文件&#xff0c;去配置我们的程序&#xff0c;比如数据库地址、网络地址等信息&…

AI终端设备的自动化分级

摘要&#xff1a; 大语言模型&#xff08;LLM&#xff09;被认为是通用人工智能&#xff08;AGI&#xff09;的潜在火花&#xff0c;为构建通用人工智能代理带来了希望。在此基础上&#xff0c;客户端设备在人工智能的帮助下不断发展&#xff0c;从基于应用程序&#xff08;AP…

【微服务】配置管理

Nacos配置管理 配置管理配置共享配置热更新 配置管理 将微服务集群中常用&#xff0c;经常变化的配置都写到一个独立的配置文件微服务中进行统一管理 配置共享 在Nacos的界面当中进行配置管理&#xff0c;在配置列表中添加配置 比如各个服务中的jdbc的连接配置&#xff1a; …

数据库系统理论——关系数据库

文章目录 一、关系&#xff08;数据结构&#xff09;1、概述2、名词解释3、关系模式、关系数据库、关系数据库模式4、基本关系的性质 二、关系操作&#xff08;数据操作&#xff09;三、关系的完整性1、实体完整性2 、参照完整性3、用户自定义的完整性 四、关系代数五、习题 前…

【编程题-错题集】非对称之美(找规律 / 贪心)

牛客对应题目链接&#xff1a;非对称之美 (nowcoder.com) 一、分析题目 找规律&#xff1a; 判断是否全都是相同字符。判断本身是否是回文。 如果这个字符串每个字符相同&#xff0c;不存在非回文子串&#xff0c;直接返回 0。如果这个字符串不是回文&#xff0c;输出字符串长度…

C++:继承-继承权限

在C中&#xff0c;类的权限分为公有、私有和保护三种。这些权限控制了类的成员&#xff08;数据成员和成员函数&#xff09;对外部代码的可见性和访问性。 公有&#xff08;public&#xff09;权限&#xff1a; 在公有权限下声明的成员可以被类的外部代码直接访问&#xff1b;公…

基于微信小程序的网上购物系统的设计与实现

基于微信小程序的网上购物系统的设计与实现 “Design and Implementation of an Online Shopping System based on WeChat Mini Program” 完整下载链接:基于微信小程序的网上购物系统的设计与实现 文章目录 基于微信小程序的网上购物系统的设计与实现摘要第一章 绪论1.1 研究…

AWTK 和 QT 资源占用不完全对比

因为没有开发两个完全一样的应用程序&#xff0c;对比的结果并不是很准确&#xff0c;仅供参考。 对比的程序为&#xff1a; AWTK demoui 演示了 AWTK 常用功能。 QT QDesktop 演示了 QT 常用功能。 运行平台为&#xff1a; i.MX6ULL Linux 1. 可以执行文件大小 1.1 AWTK…