-
反射是允許程序在運行時訪問程序結構的一類特性
-
程序結構包括:類,接口,方法,屬性等語法特性
Kotlin 字節做了一套反射API庫,這個庫需要進行依賴
implementation("org.jetbrains.kotlin:kotlin-reflect")
反射的常見用途
- 列出類型的所有方法,屬性,內部類等等
- 調用給定名稱及簽名方法或指定名稱的屬性
- 通過簽名信息獲取泛型實參的具體類型
- 訪問運行時註解及其信息完成注入或者配置操作
反射常用的數據結構
數據結構 | 概念及說明使用 |
---|---|
KType | 描述未擦除的類型或泛型參數等,例如Map<String,Int>;可以通過typeOf或者一些類型獲取對應父類,屬性,函數參數等 |
KClass | 描述對象的實際類型,不包含泛型參數,例如 Map;可通過對象,類型名直接獲得 |
KProperty | 描述屬性,可通過屬性引用,屬性所在類的 KClass 獲取 |
KFunction | 描述函數,可通過函數引用,函數所在類的 KClass 獲取 |
獲取泛型實參
fun main() {
/**
* declaredFunctions:獲取該類中所有的函數
* first:判斷是否爲指定的函數名,如果相等則返回出去
* returnType:獲取返回值
* arguments:返回值的泛型
*/
Api::class.declaredFunctions.first { it.name == "getUser" }
.returnType.arguments.forEach {
println(it.type)
}
//直接拿到函數引用,其他的和上面一樣
Api::getUser.returnType.arguments.forEach {
println(it)
}
val subType = SubType()
subType.typeParameter.let { println(it) }
}
class UserDTO
interface Api {
fun getUser(): List<UserDTO>
}
abstract class SuperType<t> {
val typeParameter by lazy {
/**
* 抽象類不能有實例,所以這裏的 this 是子類的
* supertypes:獲取父類類型列表
* first:獲取第一個
* arguments:拿到泛型
* type:類型
*/
this::class.supertypes.first().arguments.first().type!!
}
val typeParameterJava by lazy {
}
}
class SubType : SuperType<String>()
案例:深拷貝
data class Person(val id: Int, val name: String, val group: Group)
data class Group(val id: Int, val name: String, val location: String)
fun main() {
val person = Person(
0,
"345",
Group(0, "Kotlin.cn", "China")
)
val deep = person.deepCopy()
val copied = person.copy()
println(person.group === copied.group)
println(person.group === deep.group)
println(deep)
}
fun <T : Any> T.deepCopy(): T {
//如果不是數據類就直接返回
if (!this::class.isData) {
return this
}
/**
* primaryConstructor:獲取該類的主構造函數,如果沒有返回 null
*/
return this::class.primaryConstructor!!.let { primaryConstuctor ->
/**
* parameters:構造方法中的參數
*/
primaryConstuctor.parameters.map { parameter ->
/**
* memberProperties:,類中所有非擴展屬性
*/
val value = (this::class as KClass<T>).memberProperties.first { it.name == parameter.name }.get(this)
//屬性是否爲數據類
if ((parameter.type.classifier as? KClass<*>)?.isData == true) {
//如果是則進行深度拷貝
parameter to value?.deepCopy()
} else {
parameter to value
}
//call/callBy 創建對象
}.toMap().let(primaryConstuctor::callBy)
}
}
案例:映射
data class UserVO(val login: String, val avatarUrl: String)
data class UserDTO(
var id: Int,
var login: String,
var avatarUrl: String,
var url: String,
var htmlUrl: String
)
fun main() {
val userDTO = UserDTO(
0,
"345",
"https://www.baidu.com",
"https://github",
"https://httpurl"
)
val userVo: UserVO = userDTO.mapAs()
println(userVo)
val userMap = mapOf(
"id" to 0,
"login" to "1",
"avatarUrl" to "2",
"url" to "3"
)
val userVOFromMap: UserVO = userMap.mapAs()
println(userVOFromMap)
}
inline fun <reified From : Any, reified TO : Any> From.mapAs(): TO {
return From::class.memberProperties.map {
it.name to it.get(this)
}.toMap().mapAs()
}
inline fun <reified TO : Any> Map<String, Any?>.mapAs(): TO {
return TO::class.primaryConstructor!!.let {
it.parameters.map { kParameter ->
//如果接受null,則返回,否則拋出異常
//this[kParameter.name] :從當前的 map 中尋找。如果找到了則就是拿到了 value,否則異常
kParameter to (this[kParameter.name] ?: if (kParameter.type.isMarkedNullable) null
else throw IllegalArgumentException("失敗"))
}.toMap().let(it::callBy)//構建對象
}
}
案例:釋放對象引用不可空類型
class ReleasableNotNull<T : Any> {
private var value: T? = null
operator fun getValue(thisRef: Any, kProperty: KProperty<*>): T {
return value ?: throw IllegalArgumentException("使用錯誤")
}
operator fun setValue(thisRef: Any, kProperty: KProperty<*>, value: T) {
this.value = value
}
fun isInitialized() = value != null
fun release() {
value = null
}
}
//擴展屬性
inline val KProperty0<*>.isInitialized: Boolean
get() {
isAccessible = true
return (this.getDelegate() as ReleasableNotNull<*>).isInitialized()
}
//擴展方法
fun KProperty0<*>.release() {
isAccessible = true
(this.getDelegate() as ReleasableNotNull<*>).release()
}
class Bitmap(val with: Int, val height: Int)
class Activity {
private var bitmap by ReleasableNotNull<Bitmap>()
fun onCreate() {
println(::bitmap.isInitialized)
bitmap = Bitmap(234, 353)
println(::bitmap.isInitialized)
}
fun onDestroy() {
println(::bitmap.isInitialized)
::bitmap.release()
println(::bitmap.isInitialized)
}
}
fun main() {
val activity = Activity()
activity.onCreate()
activity.onDestroy()
}
false
true
true
false
通過反射調用構造函數、
fun main() {
//獲取對象的class
val cls = Person(23, "張三").javaClass.kotlin
//調用構造器 2
val cons = cls.primaryConstructor
val per1 = cons?.call(100, "李四")
println(per1)
//2
val per2 = cls.primaryConstructor.let {
var i = 0
val s = it!!.parameters.map {
i++
when (i) {
1 -> it to 2
2 -> it to "哈哈"
else ->
it to ""
}
}.toMap()
it.callBy(s)
}
println(per2)
}
class Person(val age: Int, val name: String) {
val p = 1
fun a() {}
private fun b() {}
override fun toString(): String {
return "姓名:$name 年齡:$age"
}
}
- 反射
- 反射是啥
- 數據結構:KType,KClass,KProperty,KFunction
- 獲取泛型實參
- 瞭解泛型的實現機制
- 料及實參在字節碼中的存儲機制
- 掌握反射獲取對應類型的 KType 進而讀取泛型實參的方法
- 爲數據類添加深拷貝
- 反射調用構造器構造數據類實例
- 實例之間的轉換
- 可釋放引用的不可空類型
參考自慕課網視頻