正确使用Kotlin动态代理

这篇具有很好参考价值的文章主要介绍了正确使用Kotlin动态代理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言:

说到设计模式,想必很多人都会想到,常见的设计模式之一的动态代理。特别是,对很多中高级Android程序员而言,更是如此。因为著名的网络框架Retrofit,关于网络调用部分,就是采用动态代理,将网络请求,委托给OkHttp实现。但在使用Kotlin语言,来实现动态代理时,存在一些坑。这篇博文,将为你揭开这些坑的生成原因和填坑的方法。


首先,我们先来看一个Java写法的Kotlin版动态代理,怎么实现:

第一步,我们先创建动态代理类KotlinDynamicProxy:

class KotlinDynamicProxy(private val baseProxy: BaseProxy?) : InvocationHandler {

    override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {

        try {
            if (baseProxy== null) return null
            // 动态代理调用
            method?.let { return it.invoke(baseProxy, args)  }
            return null
        } catch (e: Exception) {
            Log.d("Debug","动态代理异常:${e.message}")
        }
        return null
    }

}

下一步,我们定义个接口IAnimalProxy:

interface IAnimalProxy {
   fun noneArgs()
   fun singlePrimitiveArg(test: Int)
   fun singleLambdaArg(listener: (Int)->Unit)
   fun multipArgs(a: Int, b: Int)
}

下一步,我们创建,代理实现类AnimalProxy

class AnimalProxy:IAnimalProxy {
   override fun noneArgs(){ Log.d("Debug","noneArgs") }
   override fun singlePrimitiveArg(test: Int){ Log.d("Debug","singlePrimitiveArg") }
   override fun singleLambdaArg(listener: (Int)->Unit){ Log.d("Debug","singleLambdaArg") }
   override fun multipArgs(a: Int, b: Int){ Log.d("Debug","multipArgs") }
}

下一步,我们foo()方法里面,创建动态代理,并执行所有API的调用:

fun foo() {

     val proxy = Proxy.newProxyInstance(
            IAnimalProxy ::class.java.classLoader,
            arrayOf(IAnimalProxy ::class.java),
            BinderDynamicProxy(new AnimalProxy())
        ) as IAnimalProxy 
     noneArgs(proxy)
     singlePrimitiveArg(proxy)
     singleLambdaArg(proxy)
     multipArgs(proxy)
}

接下来,我们先来看看各个方法执行的情况:

1. noneArgs的实现和结果:

 fun noneArgs(proxy: AnimalProxy){ proxy.noneArgs() }

执行结果:成功,log正常打出,没问题。

2.singlePrimitiveArg的实现和结果:

 fun singlePrimitiveArg(proxy: AnimalProxy){ proxy.singlePrimitiveArg(1) }

执行结果:成功,log正常打出,没问题。

3.singleLambdaArg的实现和结果:

 fun singleLambdaArg(proxy: AnimalProxy){ proxy.singleLambdaArg{} }

执行结果:失败,抛出异常:

method com.xxx.AnimalProxy.singleLambdaArg argument 1 has type kotlin.jvm.functions.Function1, got java.lang.Object[]

失败的原因是,编译器会将,lambda表达式,即函数类型参数,转成kotlin的通用标准接口Function1。也就是说,编译后的代码是不同的,具体如下:

// 编译前
fun singleLambdaArg(listener: (Int)->Unit)

// 编译后
void singleLambdaArg(Function1 function1)

你可能会好奇,这个Function1是啥,Function1的本质就是一个接口,这个接口接受单个参数,这个参数是个泛型。所以,它可以完善的替换上述例子中的lambda表达式(listener: (Int)->Unit)。附带学习资料:Kotlin 高阶函数。

好了,我们回归到问题本身,即编译后的方法是这样的:

// 编译后
void singleLambdaArg(Function1 function1)

但是,我们动态代理时,传过去的参数却是个Array类型:args: Array<out Any>?

人家要个Function1参数,我们却传了个Array,那肯定不行,所以,才会抛出异常。解决方案我们先不说,继续看最后一种场景。

4.multipArgs的实现和结果:

 fun multipArgs(proxy: AnimalProxy){ proxy.multipArgs(100,200) }

执行结果:失败,抛出异常:

Wrong number of arguments; expected 2, got 1

失败的原因,很简单,因为multipArgs方法,需要2个参数,我们只却只传了一个参数。你可能会问,我不是已经传了100和200,2个参数了吗?怎么说是1个。问的好!我们再来看看,我们动态代理传参数是怎么写的:

 override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? { 
         // 省略代码
          method?.let { return it.invoke(baseProxy, args)  }
         // 省略代码
    }

我们这里,只传了一个参数args,这个参数的类型是:Array。那你可能会问,这里的Array,即args内部已经包含了100和200,2个Int参数了呀。

但实际上,如果你这个类是个Java类,则没问题,如你所想,是正确的。
但如果是个kotlin类,则有问题,编译器会认为你是要传一个Array类型的参数过去。

好了,问题和原因都解释清楚了,那我们怎么解决,3和4,两种情况的问题呢?
答案是:延展操作符(spread operator):*(星号)【个人觉得叫拆包操作符更合适】

正确的动态代理写法如下

class KotlinDynamicProxy(private val baseProxy: BaseProxy?) : InvocationHandler {

    override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {

        try {
            if (baseProxy== null) return null
            method?.let {
                // 延展操作符,不能作用在可空对象上。无参数时,直接传null即可。
                if (args == null) return it.invoke(baseProxy)
                // args ==变成==> *args
                else return it.invoke(baseProxy, *args)
            }
            return null
        } catch (e: Exception) {
            Log.d("Debug","动态代理异常:${e.message}")
        }
        return null
    }
}

关键修改it.invoke(baseProxy, args) 改成 it.invoke(baseProxy, *args)

这样修改后,
基于第3种情况,编译器,会拆包,取出Array中的Function1对象,传给method.invoke方法。
基于第4种情况,编译器,会拆包,取出Array中的两个参数,传给method.invoke方法。

好了,到这里,你应该学会了如何正确的使用Kotlin动态代理了,另外我再附上相关的学习资料:文章来源地址https://www.toymoban.com/news/detail-445361.html

  1. 延展操作符(spread operator)。
  2. Why is Kotlin throw IllegalArgumentException when using Proxy?

到了这里,关于正确使用Kotlin动态代理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • cglib动态代理、jdk动态代理及spring动态代理使用

    NickelBeforeAdvice.java方法执行之前 NickelAfterReturningAdvice.java方法执行之后 NickelAroundadvice.java环绕方法 NickelThrowAdvice.java抛异常方法 UserService.java抛异常方法 SpringProxtFactoryTest.java测试方法 NickelStaticMethodMatherPointcut.java方法匹配的方法 NickelPointcutAdvisor.java切面方法 SpringProxtFactoryTest.j

    2024年02月15日
    浏览(50)
  • 怎么使用动态代理IP提升网络安全,动态代理IP有哪些好处呢?

    目录 一、什么是动态代理IP 二、动态代理IP的优势 三、动态代理IP的代码实现 1. 安装依赖库 2. 获取代理IP 3. 使用代理IP请求目标网站 4. 动态更换代理IP 五、总结 一、什么是动态代理IP 动态代理IP是指在网络通信过程中,代理服务器不是固定IP地址,而是会不断更换IP地址的代

    2024年02月08日
    浏览(55)
  • 学习笔记-Java动态代理的简单使用

    一种设计模式 简单地说,在代理模式中存在三个角色 用户 代理 被代理的对象 用户调用代理,代理去调用被代理的对象 以此来实现功能的增强 动态代理在java中有两种实现方法 JDK中的Proxy类 CGLIB 实现InvocationHandler接口,创建自己的调用处理器 通过为Proxy类指定ClassLoader和一组

    2024年02月10日
    浏览(50)
  • 设计模式的使用——模板方法模式+动态代理模式

    一、需求介绍   现有自己写的的一套审批流程逻辑,由于代码重构,需要把以前的很多业务加上审批的功能,再执行完审批与原有业务之后,生成一个任务,然后再统一处理一个任务(本来是通过数据库作业去处理的,后来说这个任务要马上去处理,只能去统一添加一个处

    2024年02月10日
    浏览(36)
  • springAop使用的动态代理是jdk还是cglib

    Spring AOP使用的动态代理可以是JDK动态代理或CGLIB动态代理,具体选择哪种代理方式取决于被代理的目标对象的类型和配置。 当被代理的目标对象实现了至少一个接口时,Spring AOP会默认使用JDK动态代理。JDK动态代理基于接口生成代理类,通过反射机制调用目标对象的方法。 而

    2024年02月09日
    浏览(40)
  • 动态代理的两个使用方式(手动实现+SpringAOP实现)-Demo

    一、手动实现 1、具体代码 二、SpringAOP实现 1、pom文件依赖 2、具体代码

    2024年02月12日
    浏览(35)
  • 网络地址IP如何使用动态代理修改?变更IP地址是否会受到网速影响?

    在互联网的广泛应用中,有时候我们需要隐藏自己的真实IP地址,以保护隐私或规避某些网络限制。动态代理服务器技术为此提供了一种解决方案。动态代理服务器允许用户在不更改本地网络设置的情况下,通过代理服务器访问互联网,同时隐藏用户的真实IP地址。以下是使用

    2024年01月18日
    浏览(61)
  • Spring学习(五):一篇讲清楚动态代理(jdk和cglib)的使用、原理和源码

    目录 一、jdk动态代理的基本使用 二、cglib动态代理的基本使用 2.1 方法一:method.invoke() 方法反射调用 2.2 方法二(spring使用的这个方法): methodProxy.invoke() 2.3 方法三:methodProxy.invokeSuper() 三、jdk实现代理的原理  四、jdk实现代理的源码 五、jdk对代理的优化  六、cglib实现动

    2023年04月14日
    浏览(45)
  • 代理服务器可能有问题,或地址不正确(已解决)

      如果遇到电脑连上wifi,其他应用可以上网(例如可以登录QQ),但是浏览器不能浏览网页,出现 代理服务器可能有问题,或地址不正确 这个错误,具体如下图所示。   首先右击电脑wifi标志,点击 打开网络和Internet设置 ,点击 代理 ,将 自动设置代理 的 自动检测设置

    2024年02月12日
    浏览(50)
  • Android dex动态加载(Kotlin版)

    前言 环境 语言–Kotlin JDK11 SDK33 AndroidStudio版本 概述 libaray项目打包成jar jar通过dx/d8命令行工具转为dex.jar dex.jar放到assets目录下 App启动读取assets中的dex.jar复制到App可访问的文件夹中(建议内部存储的沙盒中,不受权限限制) 实例化DexClassLoader加载dex获取ClassLoader对象 通过Cla

    2023年04月12日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包