聊聊Kotlin中的元编程

简介: 聊聊Kotlin中的元编程

背景

首先还是来说下为什么出现元编程?

一个技术的出现肯定是不满足现状,那么元编程的出现是为了解决什么问题呢?举一个栗子,比如我们需要获取某个类的属性进行赋值取值或者获取函数信息进行调用时,我们当然可以编写代码以让外界访问这些数据,但是这样做容易出错而且特别麻烦,这个时候我们可以想到利用反射也可以达到同样的效果。对吧,获取类变量,函数信息这看起来就是反射可以做到的事情,所以其实反射也属于元编程范畴。

什么是元数据

顾名思义,元数据和元注解一个道理,元注解是标记注解的注解,元数据自然就是描述数据的数据,这个听起来有点绕口,来解释一些这两个“数据”到底分别指代什么?

描述“数据”的“数据”

我们知道我们的需求也就是程序是通过各种数据构建起来的,这些数据就是指类,函数,变量…等是对现实世界和需求的描述,这就是第一个数据的意思

通过类,变量,函数这些数据去描述需求程序

那么第二个数据也就知道了,是用来描述类,函数,变量的数据,这就是第二个数据的意思,也就是元数据。

通过元数据描述类,变量,函数信息

什么是元编程

直接说定义:操作元数据的编程就是指元编程。

比如我们通过反射获取类,属性,方法的一些信息,进而操作他们这也叫元编程。所以上面说到反射也算元编程的范畴。

但是这么说又太片面了,反射是通过程序获取数据,而元编程还包括通过数据获取程序。即“程序即是数据,数据即是程序”。

可以这么说元编程是更高阶的抽象,高阶函数用函数作为输入输出。而元编程用程序作为输入输出。

程序即是数据

这个很好理解,通过指定的程序来获取构成这个程序的信息,比如一个Book类,我们可以动态的获取这个类中的属性和行为,其实就是反射。

介绍

来看下Java中的反射信息结构

可以看到有参数,类,包这些信息,AccessibleObject信息代表的是可调用的元素。

比如Field指字段(仅仅代表字段),Excutable代表可执行其中包括构造函数和普通方法。

再来看下Kotlin中的反射结构:

Kclass代表类信息,Kparameter代表参数信息,而KCallable和Accessible一样代表的都是可调用的元素。

其分为两类,KFunction和KProperty,不同点是:

  • KProperty中包含普通属性和可变属性KMutableProperty,且Kotlin中的属性包含Setter和Getter方法。。而java中的Field只代表这个字段,setget是在另外一个Method结构中
  • KFunction统一了构造函数,包含Kproperty的Setter和Getter。而java中的Method还分为构造函数和普通函数,且是单独的setget方法不是Field自带的
  • Java中反射需要设置可访问性,而Kotlin中的属性自带setget方法通过get可直接获取。也就是KProprity.call(对象实例)即可获取属性。
  • Kotlin中获取信息比Java更明确直观。

Kotlin的增强

和java中的反射一样使用,不同的是Kotlin中由于多了很多特性所以其元数据类型也比java中多,比如:

metaclass描述类的类型kclass。

通过类名::class得到kclass

KClass中相比Java中的Class新增:

KCallable由于包含着KFunction和KProperty,所以先来看下KCallable中有哪些属性:

KCallable可通过KClass的members成员获取,其返回值是Collection<KCallable<*>>

通过上面的信息已经可以获取到了类,属性和方法的信息,那么我们该如何获取参数信息呢?

参数信息又分为这三种:方法的参数信息,方法的返回值信息,泛型的参数信息(也就是参数类型)。

可通过KCallable.parameters获取方法的参数信息,返回值是List< KParameter >。

KParameter新增属性:、

可看到通过Kparameter的type属性获取到参数的类型,那么返回值的类型和泛型类型该如何获取呢?

上面讲解KCallable的时候就已经有这两个属性了:

  • 返回值类型:只有方法才有返回值,所以是通过KCallable的returntype属性可以获取到
  • 参数类型:泛型一种是泛型方法还有一种是泛型类。泛型方法一样通过KCallable的typeParameters获取,在KClass中通过startProjectedType属性获取。返回值是List< KTypeParameter >不存在返回一个空的集合。

数据即是程序

这句话该怎么理解。我们倒推一下,通过一些信息来动态创建程序。

比如使用字节码工具ASM,javassist等动态生成类,还有使用KAPT注解处理器通过注解来手动输出程序到一个文件中。可以看到和Kotlin好像没有多大关系,所以Kotlin目前还没法做到动态创建程序。

注解处理器

Kotlin中的注解处理器和Java中的一样,注解参数为常量,作用范围为:

  • 基本类型
  • 字符串
  • Class对象
  • 注解
  • 类型数组,XXXArray

定义方式:比Java中更明显:用annotation修饰类即可。

使用方式:

1.添加注解处理器信息。这需要在classpath里包含META-INFO/services/javax.annotation.processing.Processor文件,并将注解处理器包名和类名写入该文件。

2.使用kapt插件。如果是gradle工程可以通过apply plugin:'kotlin-kapt’添加注解处理器支持。

kapt也支持生成Kotlin代码。

缺点

虽然annotation processor允许开发人员访问程序AST(抽象语法树可查看之前文章JVM编译只是),但没有提供行之有效的代码生成方案,目前仅有的代码生成方案也仅仅是将代码以字符串的形式写入新文件,而无法做到直接将生成的AST作为程序。这也说明了Java和Kotlin目前不具备同像性。

元编程的使用范围

1.外部程序:kotlin的语法糖suger,最终会变成java文件。所以编译器承担了 解语法糖 的角色,编译器作为外部程序去操作这些语法糖(本质也是元数据)也叫作元编程

2.获取运行时数据(反射)

3.动态执行代码(目前无法做到)

元编程需要一定的学习成本,需要了解class结构和kclass等相关程序构成的数据。


相关文章
|
存储 机器学习/深度学习 缓存
一看就懂!图解 Kotlin SharedFlow 缓存系统
一看就懂!图解 Kotlin SharedFlow 缓存系统
312 2
|
前端开发 JavaScript API
【第14期】一文读懂前端NueJS框架
【第14期】一文读懂前端NueJS框架
580 0
|
计算机视觉 C++
OpenCV-计时函数cv::getTickCount&cv::getTickFrequency
OpenCV-计时函数cv::getTickCount&cv::getTickFrequency
260 0
|
10月前
|
API Apache 对象存储
数据编排的现代时代:从数据碎片化到协作
数据工程与软件工程长期存在分歧,各自拥有独特的工具和最佳实践。本文探讨了数据编排器的角色及其最新趋势,如何使这两个领域更加紧密地结合。数据编排通过协调数据的提取、转换和服务,解决了多数据源、目标和工具的复杂性。文中介绍了 ELT 流程、不同类型的编排工具(如 Apache Airflow 和 Apache Flink),以及未来可组合数据系统的开放标准,如 Apache Parquet 和 Apache Arrow。这些标准有助于简化数据共享和治理,推动数据系统的未来发展。
265 2
数据编排的现代时代:从数据碎片化到协作
|
Shell Serverless
makefile 函数全解
makefile 函数全解
860 0
makefile 函数全解
|
11月前
|
机器学习/深度学习 数据可视化
KAN干翻MLP,开创神经网络新范式!一个数十年前数学定理,竟被MIT华人学者复活了
【10月更文挑战第12天】MIT华人学者提出了一种基于Kolmogorov-Arnold表示定理的新型神经网络——KAN。与传统MLP不同,KAN将可学习的激活函数放在权重上,使其在表达能力、准确性、可解释性和收敛速度方面表现出显著优势,尤其在处理高维数据时效果更佳。然而,KAN的复杂性也可能带来部署和维护的挑战。论文地址:https://arxiv.org/pdf/2404.19756
201 1
|
弹性计算 安全 应用服务中间件
阿里云网络系列之经典网络和专有网络
阿里云面向客户提供的网络类型服务有经典网络和专有网络两种,但这两者有什么区别呢?阿里官网给的解释是: 经典网络:IP地址由阿里云统一分配,配置简便,使用方便,适合对操作易用性要求比较高、需要快速使用 ECS 的用户。
94035 1
|
机器学习/深度学习 并行计算 PyTorch
使用PyTorch Profiler进行模型性能分析,改善并加速PyTorch训练
加速机器学习模型训练是工程师的关键需求。PyTorch Profiler提供了一种分析工具,用于测量CPU和CUDA时间,以及内存使用情况。通过在训练代码中嵌入分析器并使用tensorboard查看结果,工程师可以识别性能瓶颈。Profiler的`record_function`功能允许为特定操作命名,便于跟踪。优化策略包括使用FlashAttention或FSDP减少内存使用,以及通过torch.compile提升速度。监控CUDA内核执行和内存分配,尤其是避免频繁的cudaMalloc,能有效提升GPU效率。内存历史记录分析有助于检测内存泄漏和优化批处理大小。
1424 1
|
开发者 数据格式
【软件设计师备考 专题 】设计系统功能:系统结构和子系统
【软件设计师备考 专题 】设计系统功能:系统结构和子系统
439 0
|
存储 JavaScript 索引
数组在TypeScript中是如何工作的
数组在TypeScript中是如何工作的
162 3