Kotlin学习手记——注解,面试经历分享

}

class User

在这里插入图片描述

第一个标注注解的注解主要是指前面的@Retention@Target之类的,是写在注解类上的注解。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述在这里插入图片描述

@file:JvmName(“KotlinAnnotations”)

@file:JvmMultifileClass

package com.bennyhuo.kotlin.annotations.builtins

import java.io.IOException

@Volatile

var volatileProperty: Int = 0

@Synchronized

fun synchronizedFunction(){

}

val lock = Any()

fun synchronizedBlock(){

synchronized(lock) {

}

}

@Throws(IOException::class)

fun throwException(){

}

@Synchronized @Throws注解都是比较好用的,替代java的相应关键字,比较人性化了。其中 @file:JvmName("KotlinAnnotations")@file:JvmMultifileClass比较有意思,能让多个文件中的kotlin代码最终生成到一个类里面,假如还有一个文件如下:

@file:JvmName(“KotlinAnnotations”)

@file:JvmMultifileClass

package com.bennyhuo.kotlin.annotations.builtins

fun hello(){

}

那经过编译之后,这个文件会和上面的文件合并到一起,生成到一个kotlin类文件当中。

实例:仿 Retrofit 反射读取注解请求网络

data class User(

var login: String,

var location: String,

var bio: String)

@Retention(AnnotationRetention.RUNTIME)

@Target(AnnotationTarget.CLASS)

annotation class Api(val url: String)

@Retention(AnnotationRetention.RUNTIME)

@Target(AnnotationTarget.CLASS)

annotation class Path(val url: String = “”)

@Retention(AnnotationRetention.RUNTIME)

@Target(AnnotationTarget.FUNCTION)

annotation class Get(val url: String = “”)

@Retention(AnnotationRetention.RUNTIME)

@Target(AnnotationTarget.VALUE_PARAMETER)

annotation class PathVariable(val name: String = “”)

@Retention(AnnotationRetention.RUNTIME)

@Target(AnnotationTarget.VALUE_PARAMETER)

annotation class Query(val name: String = “”)

@Api(“https://api.github.com”)

interface GitHubApi {

@Api(“users”)

interface Users {

@Get(“{name}”)

fun get(name: String): User

@Get(“{name}/followers”)

fun followers(name: String): List

}

@Api(“repos”)

interface Repos {

@Get(“{owner}/{repo}/forks”)

fun forks(owner: String, repo: String)

}

}

object RetroApi {

const val PATH_PATTERN = “”“({(\w+)})”“”

val okHttp = OkHttpClient()

val gson = Gson()

val enclosing = {

cls: Class<*> ->

var currentCls: Class<*>? = cls

sequence {

while(currentCls != null){

// enclosingClass获取下一个class

// yield将对象添加到正在构建的sequence序列当中

currentCls = currentCls?.also { yield(it) }?.enclosingClass

}

}

}

//内联特化

inline fun create(): T {

val functionMap = T::class.functions.map{ it.name to it }.toMap() //【函数名,函数本身】的Pair转成map

val interfaces = enclosing(T::class.java).takeWhile { it.isInterface }.toList() //拿到所有接口列表

println("interfaces= i n t e r f a c e s " ) / / 输出 [ G i t H u b A p i interfaces")// 输出 [GitHubApi interfaces")//输出[GitHubApiUsers, GitHubApi]

//foldRight从interfaces序列的右边开始拼

val apiPath = interfaces.foldRight(StringBuilder()) {

clazz, acc ->

// 拿到每个接口类的Api注解的url参数值,如果url参数为空,则使用类名作为url值

acc.append(clazz.getAnnotation(Api::class.java)?.url?.takeIf { it.isNotEmpty() } ?: clazz.name)

.append(“/”)

}.toString()

println(“apiPath= $apiPath”) // https://api.github.com/users/

//动态代理

return Proxy.newProxyInstance(RetroApi.javaClass.classLoader, arrayOf(T::class.java)) {

proxy, method, args ->

//所有函数中的抽象函数 即接口的方法

functionMap[method.name]?.takeIf { it.isAbstract }?.let {

function ->

//方法的参数

val parameterMap = function.valueParameters.map {

//参数名和参数的值放在一起

it.name to args[it.index - 1] //valueParameters包含receiver 因此需要index-1来对应args

}.toMap()

println(“parameterMap= $parameterMap”) //{name=bennyhuo}

//{name} 拿到Get注解的参数 如果注解参数不为空就使用注解参数,如果为空使用方法名称

val endPoint = function.findAnnotation()!!.url.takeIf { it.isNotEmpty() } ?: function.name

println(“endPoint= $endPoint”) //{name}/followers

//正则找到endPoint中的所有符合"{owner}/{repo}/forks"其中{xxx}的结果

val compiledEndPoint = Regex(PATH_PATTERN).findAll(endPoint).map {

matchResult ->

println(“matchResult.groups= ${matchResult.groups}”) // [MatchGroup(value={name}, range=0…5), MatchGroup(value={name}, range=0…5), MatchGroup(value=name, range=1…4)]

println(“matchResult.groups1.range= ${matchResult.groups[1]?.range}”) // 0…5

println(“matchResult.groups2.value= ${matchResult.groups[2]?.value}”) // name

matchResult.groups[1]!!.range to parameterMap[matchResult.groups[2]!!.value]

}.fold(endPoint) {

acc, pair ->

//acc的初始值就是endPoint即{name}/followers

println(“acc= ${acc}”) // {name}/followers

println(“pair= ${pair}”) // (0…5, bennyhuo) pair是一个 range to name

acc.replaceRange(pair.first, pair.second.toString()) // 把{name}/followers中的0到5的位置的字符串{name}替换成bennyhuo

}

println(“compiledEndPoint= ${compiledEndPoint}”) //bennyhuo/followers

//拼接api和参数

val url = apiPath + compiledEndPoint

println(“url ==== $url”)

println(“*****************”)

okHttp.newCall(Request.Builder().url(url).get().build()).execute().body()?.charStream()?.use {

gson.fromJson(JsonReader(it), method.genericReturnType)//返回json的解析结果

}

}

} as T

}

}

fun main() {

//interface com.bennyhuo.kotlin.annotations.eg.GitHubApi

//println(“enclosingClass=${GitHubApi.Users::class.java.enclosingClass}”)

val usersApi = RetroApi.create<GitHubApi.Users>()

val user = usersApi.get(“bennyhuo”)

val followers = usersApi.followers(“bennyhuo”).map { it.login }

println(“user ====== $user”)

println(“followers ======== $followers”)

}

这个例子还是有点复杂,不太好理解,有些方法没接触过不知道啥意思,这里加了很多打印方法,把结果打印输出一下,这样能知道具体是代表的啥,就好理解一点了。

实例:注解加持反射版 Model 映射

这个例子是在前面反射一节实现的model映射例子的基础上,通过添加注解方式处理那些字段名称不是相同风格的情况,比如两个对象中的avatar_urlavatarUrl的相互映射。

//不写默认是RUNTIME

//@Retention(AnnotationRetention.RUNTIME)

@Target(AnnotationTarget.VALUE_PARAMETER)

annotation class FieldName(val name: String)

@Target(AnnotationTarget.CLASS)

annotation class MappingStrategy(val klass: KClass)

interface NameStrategy {

fun mapTo(name: String): String

}

//下划线转驼峰

object UnderScoreToCamel : NameStrategy {

// html_url -> htmlUrl

override fun mapTo(name: String): String {

//先转成字符数组,然后fold操作

return name.toCharArray().fold(StringBuilder()) { acc, c ->

when (acc.lastOrNull()) { //上一次的acc不是空

‘_’ -> acc[acc.lastIndex] = c.toUpperCase() //上一次结果的最后一个字符是下划线就把下划线位置替换成当前字符的大写字母

else -> acc.append© // 否则直接拼接

}

//返回acc

acc

}.toString()

}

}

//驼峰转下划线

object CamelToUnderScore : NameStrategy {

override fun mapTo(name: String): String {

//先转成字符数组,然后fold操作

return name.toCharArray().fold(StringBuilder()) { acc, c ->

when {

c.isUpperCase() -> acc.append(‘_’).append(c.toLowerCase()) //如果是大写字母直接拼一个下划线再拼上小写

else -> acc.append©

}

//返回acc

acc

}.toString()

}

}

//使用定义的策略注解,驼峰转下划线

@MappingStrategy(CamelToUnderScore::class)

data class UserVO(

val login: String,

//@FieldName(“avatar_url”) //这种是单个字段上面添加注解,只能一个一个添加

val avatarUrl: String,

var htmlUrl: String

)

data class UserDTO(

var id: Int,

var login: String,

var avatar_url: String,

var url: String,

var html_url: 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”,

“avatar_url” to “https://api.github.com/users/bennyhuo”,

“html_url” to “https://github.com/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 Map<String, Any?>.mapAs(): To {

return To::class.primaryConstructor!!.let {

it.parameters.map { parameter ->

parameter to (this[parameter.name]

// let(this::get)等价于let{this[it]} userDTO[“avatar_url”]

?: (parameter.annotations.filterIsInstance().firstOrNull()?.name?.let(this::get))

// 拿到UserVO类的注解MappingStrategy的kclass即CamelToUnderScore,它是一个object calss, objectInstance获取实例,然后调用mapTo把avatarUrl转成avatar_url,最后调用userDTO[“avatar_url”]

?: To::class.findAnnotation()?.klass?.objectInstance?.mapTo(parameter.name!!)?.let(this::get)

?: if (parameter.type.isMarkedNullable) null

else throw IllegalArgumentException(“${parameter.name} is required but missing.”))

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

结尾

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

这里放上一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套进阶学习的视频及面试专题资料包,在这里免费分享给大家,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家~

iTtPVf-1710667698093)]
[外链图片转存中…(img-7bVlQfVM-1710667698093)]
[外链图片转存中…(img-QUCh3R0b-1710667698093)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-gHWJNuQ6-1710667698094)]

结尾

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

这里放上一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套进阶学习的视频及面试专题资料包,在这里免费分享给大家,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家~

[外链图片转存中…(img-zoKOKYV5-1710667698094)]

  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值