前段时间有人问我:「你是怎么成为一名软件架构师的?」我们就此探讨了必备技能、经验,以及储备相关知识所需的时间和精力。除此之外,我也回顾了自己走过的路、使用或尝试过的技术,以及我从那些五花八门的工作中学到的东西。
//可以看到后续调用是直接用了上次的缓存结果
println(lazyValue)
}
非空属性: 适用于那些无法在初始化阶段确认属性值的场合。lateinit 修饰符只能在类(不在主构造函数中)内声明的var 属性上使用,而且只有在该属性没有自定义集合或者设置器时,此外属性的类型必须是非空的,并且不能是基元类型。而非空属性没有这些限制。其他他们的作用是相同的。如下是一个案例:
class Tree {
//非空属性解决了 var name: String? = null 导致后续判断冗余
//非空属性解决了 var name: String = “” 初值隐晦问题
//非空属性解决了 lateinit 的一些缺陷,譬如 lateinit 只能应用于非基元类型,譬如不能用于 Int 等问题
var name: String by Delegates.notNull()
}
/**
调用
*/
fun testRun() {
val tree = Tree()
//运行时异常,没有赋值而使用 IllegalStateException: Property name should be initialized before get.
//println(tree.name)
tree.name = “123”
println(tree.name)
}
可观测属性: kotlin 提供了 observable 赋值后观测器和 vetoable 赋值前拦截观测器的能力。如下是一个案例:
class Tree {
//可观测属性初值,10 是属性初值,当属性被赋值后会触发回调 lambada 表达式
var age: Int by Delegates.observable(10) {
property, oldValue, newValue ->
println(“property name is ${property.name}, old value is $oldValue, new value is $newValue”)
}
//可观测属性初值,10 是属性初值,当属性被赋值前会触发回调 lambada 表达式,可以做到类似属性值赋值拦截器的效果
//这里当赋值小于等于0则丢弃
var defaultCount: Int by Delegates.vetoable(10) {
property, oldValue, newValue -> when {
newValue <= 0 -> {
println(“unsupport value of $newValue, refused!”)
return@vetoable false
}
else -> {
println(“property name is ${property.name}, old value is $oldValue, new value is $newValue”)
return@vetoable true
}
}
}
}
/**
调用结果
property name is age, old value is 10, new value is 10
property name is age, old value is 10, new value is 11
property name is age, old value is 11, new value is 12
12
property name is defaultCount, old value is 10, new value is 100
unsupport value of -2, refused!
property name is defaultCount, old value is 100, new value is 101
101
*/
fun testRun() {
val tree = Tree()
tree.age = 10
tree.age = 11
tree.age = 12
println(tree.age)
println(“-------------”)
tree.defaultCount = 100
tree.defaultCount = -2
tree.defaultCount = 101
println(tree.defaultCount)
}
map 委托: 可以将属性值存储到 map 当中。通常出现在 json 解析或者一些动态行为,在这种情况中可以将 map 实例作为类中属性的委托。注意:map 中的 key 名字必须要和属性的名字一致才行,否则委托后运行解析时会抛出 NoSuchElementException 异常提示 key name 不存在。下面是只读 map 属性委托的案例:
class Result (map: Map<String, Any?>) {
val name: String by map
val address: String by map
val age: Int by map
val date: Date by map
override fun toString(): String {
return "{name: ${this.name}, address: ${this.address}, " +
“age: ${this.age}, date: ${this.date}}”
}
}
/**
调用结果
{name: ruoshui, address: zhuhai, age: 18, date: Sun Oct 18 14:50:21 CST 2019}
*/
fun testRun() {
val result = Result(mapOf(
“name” to “ruoshui”,
“address” to “zhuhai”,
“age” to 18,
“date” to Date()
))
println(result.toString())
}
下面是读写 map 属性委托的案例(可以看到背后都是交给 map 存储的,写属性后 map 会跟着变化):
class Result (map: MutableMap<String, Any?>) {
var name: String by map
var address: String by map
var age: Int by map
var date: Date by map
override fun toString(): String {
return "{name: ${this.name}, address: ${this.address}, " +
“age: ${this.age}, date: ${this.date}}”
}
}
/**
调用结果
{name: ruoshui, address: zhuhai, age: 18, date: Sun Oct 18 14:57:25 CST 2020}
ruoshui
{name: gongjiang, address: zhuhai, age: 18, date: Sun Oct 18 14:57:25 CST 2020}
gongjiang
*/
fun testRun() {
val map: MutableMap<String, Any?> = mutableMapOf(
“name” to “ruoshui”,
“address” to “zhuhai”,
“age” to 18,
“date” to Date()
)
val result = Result(map)
println(result.toString())
println(map[“name”])
println(“--------”)
result.name = “gongjiang”
println(result.toString())
println(map[“name”])
}
对于每个委托属性来说,kotlin 编译器在底层会生成一个辅助的属性,然后将原有属性的访问委托给这个辅助属性。比如说,对于属性 prop 来说,kotlin 编译器所生成的隐含属性为prop$delete
,然后对原有的 prop 属性的访问器的访问都只是委托给了这个额外的辅助属性。
[](()提供委托(providing a delegate)
通过定义 provideDelegate operator,我们可以扩展委托的创建过程逻辑。如果对象定义了 provideDelegate 方法,那么该方法就会被调用来创建属性的委托实例。下面是一个案例:
//委托实现类
class PropertyDelete: ReadOnlyProperty<AnimBase, String> {
override fun getValue(thisRef: AnimBase, property: KProperty<*>): String {
return “$thisRef, your deleted property name is ${property.name}”
}
}
//提供委托实现:作用是当满足特定条件才给属性添加委托
class AnimBaseLauncher {
operator fun provideDelegate(thisRef: AnimBase, property: KProperty<*>): ReadOnlyProperty<AnimBase, String> {
println(“AnimBaseLauncher provideDelegate invoke…”)
//这里可以类似工厂依据不同条件生产不同的委托属性或者处理逻辑
when (property.name) {
“name”, “address” -> return PropertyDelete()
else -> throw Exception(“property name not valid!”)
}
}
}
//普通类
class AnimBase {
val name: String by AnimBaseLauncher()
val address: String by AnimBaseLauncher()
//打印输出第二个 AnimBaseLauncher provideDelegate invoke… 后报错 property name not valid!
//val work: String by AnimBaseLauncher()
}
/**
调用结果:
AnimBaseLauncher provideDelegate invoke…
AnimBaseLauncher provideDelegate invoke…
cn.yan.test.AnimBase@6e5e91e4, your deleted property name is name
cn.yan.test.AnimBase@6e5e91e4, your deleted property name is address
*/
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》开源
写在最后
最后我想说:对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!
这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
相信它会给大家带来很多收获:
这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
相信它会给大家带来很多收获:
[外链图片转存中…(img-CuomqSMn-1649666229103)]
[外链图片转存中…(img-WG0LI6zY-1649666229103)]