不会真的学不会kotlin吧?(七)反射

本文是根据慕课网kotlin相关课程本人笔记和个人理解所作
代码部分参考慕课网新kotlin入门课程

文章目录

    • 本章简介
    • 结构导图
    • 反射
    • 获取泛型实参
    • DeepCopy
    • Model映射
    • 释放对象不可空引用的类型
    • 插件化加载类

本章简介

这一章主要就是要注意java类和kotlin反射的区别

结构导图

不会真的学不会kotlin吧?(七)反射_第1张图片

反射

不会真的学不会kotlin吧?(七)反射_第2张图片

数据结构 概念及使用说明
KType 描述未擦除的类型或泛型参数等,例如Map; 可通过typeOf或者以下类型获取对应的父类、属性、函数参数等
KClass 描述对象的实际类型,不包含泛型参数,例如Map; 可通过对象、类型名直接获得
KProperty 描述属性,可通过属性引用、属性所在类的KClass获取
KFunction 描述函数,可通过函数引用、函数所在类的KClass获取

java

public class JavaReflections {
    public static void main(String... args) throws NoSuchFieldException {
        Class<String> cls = String.class;

        Class<? extends Object> clsOfObj = String.class;

        Field field = cls.getDeclaredField("value");

    }
}

反射实现

kotlin.Metadata这个注解,Kotlin 反射过程中,注解的内容解析之后会实例化一个叫做 KotlinClassHeader 的类。

Metadata KotlinClassHeader 说明
k kind 注解标注目标类型,例如类、文件等等
mv metadataVersion 该元数据的版本
bv bytecodeVersion 字节码版本
d1 data 自定义元数据
d2 strings 自定义元数据补充字段
xs extraString 附加字段
xi extraInt 1.1 加入的附加标记,标记类文件的来源类型
  • d1:存储了自定义格式的元数据,官方声称针对不同的类型格式不定,甚至可以为空,研究发现目前采用 Protobuf 进行序列化存储。这些数据会被 Kotlin 反射读取,是反射的一个非常重要的数据来源。其中包含不限于类型、函数、属性等的可见性、类型是否可空、函数是否为 suspend等等信息。
  • d2:存储明文字符串字面量,主要存储 Jvm 签名等信息。之所以这样设计,主要是为了将这些字符串在运行时直接加载到虚拟机内存的常量池中予以复用,减少内存开销。

kotlin

@ExperimentalStdlibApi
fun main() {
    var cls: KClass<String> = String::class



    val property = cls.declaredMemberProperties.firstOrNull()

    val mapCls = Map::class
    println(mapCls)
    val mapType = typeOf<Map<String, Int>>()

    mapType.arguments.forEach {
        println(it)
    }
}

object B {
    fun hello(){

    }
}

class A {
    fun String.hello(){

    }
}

open class Super<T>

class Sub: Super<String>()

获取泛型实参

kotlin

interface Api {
    fun getUsers(): List<UserDTO>
}

abstract class SuperType<T> {
    val typeParameter by lazy {
        this::class.supertypes.first().arguments.first().type!!
    }

    val typeParameterJava by lazy {
        this.javaClass.genericSuperclass.safeAs<ParameterizedType>()!!.actualTypeArguments.first()
    }
}

class SubType : SuperType<String>()

fun main() {
    Api::class.declaredFunctions.first { it.name == "getUsers" }
        .returnType.arguments.forEach {
        println(it)
    }

    Api::getUsers.returnType.arguments.forEach {
        println(it)
    }

    Api::class.java.getDeclaredMethod("getUsers")
        .genericReturnType.safeAs<ParameterizedType>()?.actualTypeArguments?.forEach {
        println(it)
    }

    val subType = SubType()
    subType.typeParameter.let(::println)
    subType.typeParameterJava.let(::println)
}

fun <T> Any.safeAs(): T? {
    return this as? T
}

DeepCopy

kotlin

fun <T : Any> T.deepCopy(): T {
    if(!this::class.isData){
        return this
    }

    return this::class.primaryConstructor!!.let {
        primaryConstructor ->
        primaryConstructor.parameters.map { parameter ->
            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
            }
        }.toMap()
            .let(primaryConstructor::callBy)
    }
}

//region demo
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,
        "Bennyhuo",
        Group(
            0,
            "Kotliner.cn",
            "China"
        )
    )

    val copiedPerson = person.copy()
    val deepCopiedPerson = person.deepCopy()

    println(person === copiedPerson) //false
    println(person === deepCopiedPerson) //false

    println(person.group === copiedPerson.group) //true for shallow copy.
    println(person.group === deepCopiedPerson.group) //false

    println(deepCopiedPerson)
}
//endregion

Model映射

kotlin

//mapping fields. Using Annotations next chapter.
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,
        "Bennyhuo",
        "https://avatars2.githubusercontent.com/u/30511713?v=4",
        "https://api.github.com/users/bennyhuo",
        "https://github.com/bennyhuo"
    )

    val userVO: UserVO = userDTO.mapAs()
    println(userVO)

    val userMap = mapOf(
        "id" to 0,
        "login" to "Bennyhuo",
        "avatarUrl" to "https://api.github.com/users/bennyhuo",
        "url" to "https://api.github.com/users/bennyhuo"
    )

    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 {
            parameter ->
            parameter to (this[parameter.name] ?: if(parameter.type.isMarkedNullable) null
            else throw IllegalArgumentException("${parameter.name} is required but missing."))
        }.toMap()
            .let(it::callBy)
    }
}

释放对象不可空引用的类型

kotlin

//region impl
fun <T : Any> releasableNotNull() = ReleasableNotNull<T>()

class ReleasableNotNull<T: Any>: ReadWriteProperty<Any, T> {
    private var value: T? = null

    override fun getValue(thisRef: Any, property: KProperty<*>): T {
        return value ?: throw IllegalStateException("Not initialized or released already.")
    }

    override fun setValue(thisRef: Any, property: 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()
            ?: throw IllegalAccessException("Delegate is not an instance of ReleasableNotNull or is null.")
    }

fun KProperty0<*>.release() {
    isAccessible = true
    (this.getDelegate() as? ReleasableNotNull<*>)?.release()
        ?: throw IllegalAccessException("Delegate is not an instance of ReleasableNotNull or is null.")
}

//endregion

//region demo
class Bitmap(val width: Int, val height: Int)

class Activity {
    private var bitmap by releasableNotNull<Bitmap>()

    fun onCreate(){
        println(this::bitmap.isInitialized)
        bitmap = Bitmap(1920, 1080)
        println(::bitmap.isInitialized)
    }

    fun onDestroy(){
        println(::bitmap.isInitialized)
        ::bitmap.release()
        println(::bitmap.isInitialized)
    }
}

fun main() {
    val activity = Activity()
    activity.onCreate()
    activity.onDestroy()
}
//endregion

插件化加载类

kotlin

class PluginClassLoader(private val classPath: String) : URLClassLoader(arrayOf(File(classPath).toURI().toURL())) {
    init {
        println("Classloader init @${hashCode()}")
    }

    protected fun finalize() {
        println("Classloader will be gc @${hashCode()}")
    }
}

class PluginLoader(private val classPath: String) {

    private val watcher = FileWatcher(
        File(classPath),
        onCreated = ::onFileChanged,
        onModified = ::onFileChanged,
        onDeleted = ::onFileChanged
    )

    private var classLoader: PluginClassLoader? = null
    private var plugin: Plugin? = null

    fun load() {
        reload()
        watcher.start()
    }

    private fun onFileChanged(file: File) {
        println("$file changed, reloading...")
        reload()
    }

    @Synchronized
    private fun reload() {
        plugin?.stop()
        this.plugin = null
        this.classLoader?.close()
        this.classLoader = null

        val classLoader = PluginClassLoader(classPath)
        val properties = classLoader.getResourceAsStream(Plugin.CONFIG)?.use {
            Properties().also { properties ->
                properties.load(it)
            }
        } ?: run {
            classLoader.close()
            return println("Cannot find config file for $classPath")
        }

        plugin = properties.getProperty(Plugin.KEY)?.let {
            val pluginImplClass = classLoader.loadClass(it) as? Class<Plugin>
                ?: run {
                    classLoader.close()
                    return println("Plugin Impl from $classPath: $it should be derived from Plugin.")
                }

            pluginImplClass.kotlin.primaryConstructor?.call()
                ?: run {
                    classLoader.close()
                    return println("Illegal! Plugin has no primaryConstructor!")
                }
        }

        plugin?.start()
        this.classLoader = classLoader

        System.gc()
    }

}


fun main() {
    arrayOf(
        "plugin-1/build/libs/plugin-1-1.0-SNAPSHOT.jar",
        "plugin-2/build/libs/plugin-2-1.0-SNAPSHOT.jar"
    ).map {
        PluginLoader(it).also { it.load() }
    }
}

文件监听

typealias FileEventListener = (file: File) -> Unit
private val EMPTY: FileEventListener = {}

class FileWatcher(private val watchFile: File,
                  private val recursively: Boolean = true,
                  private val onCreated: FileEventListener = EMPTY,
                  private val onModified: FileEventListener = EMPTY,
                  private val onDeleted: FileEventListener = EMPTY) {

    private val folderPath by lazy {
        Paths.get(watchFile.canonicalPath).let {
            if (Files.isRegularFile(it)) it.parent else it
        } ?: throw IllegalArgumentException("Illegal path: $watchFile")
    }

    @Volatile
    private var isWatching = false

    private fun File.isWatched() = watchFile.isDirectory || this.name == watchFile.name

    private fun Path.register(watchService: WatchService, recursively: Boolean, vararg events: Kind<Path>) {
        when (recursively && watchFile.isDirectory) {
            true -> // register all subfolders
                Files.walkFileTree(this, object : SimpleFileVisitor<Path>() {
                    override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult {
                        dir.register(watchService, *events)
                        return FileVisitResult.CONTINUE
                    }
                })
            false -> register(watchService, *events)
        }
    }

    @Synchronized
    fun start() {
        if(!isWatching){
            isWatching = true
        }

        thread {
            // We obtain the file system of the Path
            val fileSystem = folderPath.fileSystem

            // We create the new WatchService using the try-with-resources block(in kotlin we use `use` block)
            fileSystem.newWatchService().use { service ->
                // We watch for modification events
                folderPath.register(service, recursively,
                    StandardWatchEventKinds.ENTRY_CREATE,
                    StandardWatchEventKinds.ENTRY_DELETE,
                    StandardWatchEventKinds.ENTRY_MODIFY
                )

                // Start the infinite polling loop
                while (isWatching) {
                    // Wait for the next event
                    val watchKey = service.take()

                    watchKey.pollEvents().forEach { watchEvent ->
                        // Get the type of the event
                        Paths.get(folderPath.toString(), (watchEvent.context() as Path).toString()).toFile()
                            .takeIf {
                                it.isWatched()
                            }
                            ?.let(
                                when (watchEvent.kind()) {
                                    StandardWatchEventKinds.ENTRY_CREATE -> {
                                        println("onCreated")
                                        onCreated
                                    }
                                    StandardWatchEventKinds.ENTRY_DELETE -> {
                                        println("onDeleted")
                                        onDeleted
                                    }
                                    else ->{
                                        println("onModified")
                                        onModified // modified.
                                    }
                                }
                            )
                    }

                    if (!watchKey.reset()) {
                        // Exit if no longer valid
                        break
                    }
                }
            }
        }
    }

    @Synchronized
    fun stop(){
        isWatching = false
    }
}

你可能感兴趣的:(不会真的学不会kotlin把?)