本文是根据慕课网kotlin相关课程本人笔记和个人理解所作
代码部分参考慕课网新kotlin入门课程
这一章主要就是要注意java类和kotlin反射的区别
数据结构 | 概念及使用说明 |
---|---|
KType | 描述未擦除的类型或泛型参数等,例如Map |
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 加入的附加标记,标记类文件的来源类型 |
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
}
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
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
}
}