表达式思维
在 Kotlin 当中,Any 是所有类型的父类,我们可以称之为根类型
Nothing 就是 Kotlin 所有类型的子类型。Nothing 才是底类型,而“Nothing?”则不是底类型。
不变性思维
Mutable -可变的
集合是可变的(比如 MutableList),不可变的(比如 List)
- 第一条准则:尽可能使用条件表达式消灭 var
- 第二条准则:使用数据类来存储数据,消灭数据类的可变性。
class Person {
var name: String? = null
var age: Int? = 0
}
我们可以将 var 都改为 val,就像下面这样:
// var -> val
data class Person(
val name: String?,
val age: Int?
)
而到这里,你可能就会产生一个疑问:Person 的所有属性都改为 val 以后,万一想要修改它的值该怎么办呢? 比如说,直接修改它的值的话,这段代码就会报错:
class ImmutableExample {
// 修改Person的name,然后返回Person对象
fun changeUserName(person: Person, newName: String): Person {
person.name = newName // 报错,val无法修改
return person
}
}
这一点也是我们要尤为注意的:我们从 Java、C 那边带来的习惯,会促使我们第一时间想到上面这样的解决方案。但实际上,Kotlin 更加推崇我们用下面的方式来解决:
class ImmutableExample {
fun changeUserName(person: Person, newName: String): Person =
person.copy(name = newName)
}
注意:使用copy()方法, 每次都会创建一个新的对象, 如果不注意在for循环里面使用该方法, 很可能会造成内存抖动
在这段代码中,我们并没有直接修改参数 person 的值,而是返回了一个新的 person 对象。我们借助数据类的 copy 方法,快速创建了一份拷贝的同时,还完成了对 name 属性的修改。类似这样的代码模式,就可以极大地减少程序出 Bug 的可能。
- 第三条准则:尽可能对外暴露只读集合
//方式1
class Model {
val data: List<String> by ::_data
private val _data: MutableList<String> = mutableListOf()
fun load() {
_data.add("Hello")
}
}
//方式2
class Model {
val data: List<String>
get() = _data // 自定义get
private val _data: MutableList<String> = mutableListOf()
fun load() {
_data.add("Hello")
}
}
//方式3
class Model {
private val data: MutableList<String> = mutableListOf()
fun load() {
data.add("Hello")
}
// 变化在这里
fun getData(): List<String> = data
}
以上这三种方式,本质上都是对外暴露了一个“不可变的集合”,完成了可变性的封装
- 第四条准则:只读集合底层不一定是不可变的,要警惕 Java 代码中的只读集合访问行为。
class Model1 {
val list: List<String> = listOf("hello", "world")
}
public List<String> test() {
Model model = new Model();
List<String> data = model.getData();
data.set(0, "Some Data"); // 抛出异常 UnsupportedOperationException
return data;
}
public class ImmutableJava {
public List<String> test1() {
Model1 model = new Model1();
List<String> data = model.getList();
System.out.println(data.get(0));
data.set(0, "some data"); // 注意这里
System.out.println(data.get(0));
return data;
}
}
// 结果
hello
some data
我们在 Java 代码当中调用 data.set() 方法,并没有引起异常,程序也正常执行完毕,并且结果也符合预期。在这种情况下,Kotlin 的 List 被编译器转换成了 java.util.ArraysArrayList,甚至还可能会变成 java.util.ArrayList。在这里,我们完全不必去深究编译器背后的翻译规则,我们只需要时刻记住,Kotlin 当中的只读集合,在 Java 看来和普通的可变集合是一样的
- 第五条准则:val 并不意味着绝对的不可变
object TestVal {
val a: Double
get() = Random.nextDouble()
fun testVal() {
println(a)
println(a)
}
}
// 结果
0.0071073054825220305
0.6478886064282862
空安全思维
interface Callback<T: Any> {
fun onSuccess(data: T)
fun onFail(throwable: Throwable)
}
泛型边界“T: Any”保证 T 类型一定是非空的。
fun <T> saveSomething(data: T) {}
// ↑
// 等价
// ↓
fun <T: Any?> saveSomething(data: T) {}
// 增加泛型的边界限制
// ↓
fun <T: Any> saveSomething(data: T) {
val set = sortedSetOf<T>()
set.add(data)
}
fun main() {
// 编译无法通过
// ↓
saveSomething(null)
}
Log工具类:
@file:JvmName("LiLog")
package com.lixiang.car.weather.util
import android.util.Log
const val LITAG = "WeatherApp."
inline fun Any.logd(msg: String) {
Log.d(LITAG + this::class.java.simpleName, msg)
}
inline fun Any.logd(mTag: String?, msg: String) {
var tag = LITAG + this::class.java.simpleName
mTag?.let {
tag += ".$it"
}
Log.d(tag, msg)
}
inline fun Any.logi(msg: String) {
Log.i(LITAG + this::class.java.simpleName, msg)
}
inline fun Any.logw(msg: String) {
Log.w(LITAG + this::class.java.simpleName, msg)
}
inline fun Any.loge(msg: String) {
Log.e(LITAG + this::class.java.simpleName, msg)
}
使用方法
Kotlin:
logd("WeatherDataModel 3.2+++++TEST+++++++")
logd("TEST", "WeatherDataModel 3.2+++++TEST+++++++")
Java:
LiLog.logd(this,"LocationCompatVL logw3+++++TEST+++++++");
LiLog.logd(this,"TAG","LocationCompatVL logw3+++++TEST+++++++");
输出:
D WeatherApp.LocationCompatVL: LocationCompatVL logw3+++++TEST+++++++
D WeatherApp.LocationCompatVL.TAG: LocationCompatVL logw3+++++TEST+++++++
D WeatherApp.WeatherDataModel: WeatherDataModel 3.2+++++TEST+++++++
D WeatherApp.WeatherDataModel.TEST: WeatherDataModel 3.2+++++TEST+++++++