聊聊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中的元编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 谈谈mysql的悲观和乐观锁 - 周伯通
- 下一篇: 新闻 华为 鸿蒙,华为鸿蒙系统或6月正式