《kotlin核心编程》读书总结
kotlin常用表达式,if、when、try等,其中if表达式如果要达到三目运用,只需要在语句中加上else即可,例如:
val value = if (true) 1 else 2
kotlin中unit是一个类型,而不是一个关键字,例如一个方法没有显示声明返回值,那么他就返回unit类型,这个类似void的作用。
表达式函数体
单行加等号的函数声明称为表达式函数体,例如:
// testFun 是 toString 的结果,直接调用相当于调用toString
val testFun = toString()
// 这个是kotlin deCompile之后的翻译,这个就相当于testFun存起来函数执行之后的结果而已
@NotNull
private final String testFun = this.toString();
// 表达式函数体
fun testF() = toString()
fun testF2(a: Int, b : Int) = a + b
// 高阶函数,传入函数作为参数,然后在这个函数里面执行函数
fun useFF(c: Int, result: (Int, Int) -> Int){
Log.d(KSTAG, "c is $c result is ${c + result(c, c)}")
}
// 用法,注意这里是不需要再次传入a b类型的,因为定义的哪里已经明确了为Int了
val useFFF = useFF(2) { a ,b ->
a*b
}
useFF(100) { a ,b ->
a*b - a - b - b -b
}
// 用法结束
// 翻译为Java
public final void useFF(int c, @NotNull Function2 result) {
Intrinsics.checkNotNullParameter(result, "result");
Log.d("KS_TAG", "c is " + c + " result is " + (c + ((Number)result.invoke(c, c)).intValue()));
}
// 7 params
fun useFF(c: Int, result: (Int, Int, Int, Int, Int, Int, Int) -> Int){
Log.d(KSTAG, "c is $c result is ${c + result(c, c, c, c, c, c,c)}")
}
// trans
public final void useFF(int c, @NotNull Function7 result) {
Intrinsics.checkNotNullParameter(result, "result");
Log.d("KS_TAG", "c is " + c + " result is " + (c + ((Number)result.invoke(c, c, c, c, c, c, c)).intValue()));
}
// 查看定义最多22个哈哈哈
Function22
kotlin数组
// 拥有原生的各种Array
val intA = IntArray(10)
// 对象类型的需要泛型指定
val stringsOrNulls = arrayOfNulls<String>(10) // returns Array<String?>
val someStrings = Array<String>(5) { "it = $it" }
val otherStrings = arrayOf("a", "b", "c")
// kotlin定义
val a : Array<String?> = Array(1, {null})
// 翻译为Java
byte var6 = 1;
String[] var7 = new String[var6];
// kotlin的一个bug,在传入Java的可变参数的时候会强转为Object,这个导致我的动态代理不能用kotlin去写
// 这里可以看到args传入的时候是Object[]
@NotNull
public Object invoke(@Nullable Object proxy, @Nullable Method method, @Nullable Object[] args) {
String result = "JGP 代理之前: " + this.seller;
boolean var5 = false;
System.out.println(result);
result = null;
Object var10000;
if (args == null) {
Intrinsics.checkNotNull(method);
var10000 = method.invoke(this.seller);
} else {
Intrinsics.checkNotNull(method);
// 这里的invoke实际上要求的是 Object... 类型,这里应该是识别有误,把这个参数没有加kotlin的 vararg了,因此转义为 Object
var10000 = method.invoke(this.seller, (Object)args);
}
Object result = var10000;
String var8 = "JGP 代理之后: " + this.seller;
boolean var6 = false;
System.out.println(var8);
Intrinsics.checkNotNull(result);
return result;
}
kotlin的数组数据在堆内存中。
lambda表达式
- 一个lambda表达式必须使用 {} 包裹
- 如果lambda表达式声明了参数部分的类型,且返回值类型支持推导,那么lambda变量就可以省略函数变量声明
- 如果lambda返回的不是unit,那么默认返回最后一句的执行结果
// 参数定义里声明了类型,lambda函数里也声明
val sum : (Int, Int) -> Int = {x: Int, y: Int -> x+y }
// 参数不声明,lambda里面声明
val sum = {x: Int, y: Int -> x+y }
// 参数声明,lambda表达式里面省略
val sum : (Int, Int) -> Int = {x, y -> x+y }
invoke方法
fun foo(tt: Int) = {
print(tt)
}
// invoke 方法显式调用,如果不调用就是拿的print方法的引用
listOf(1, 2, 3).forEach { foo(it).invoke() }
// invoke方法也可以省略,以小括号代替
listOf(1, 2, 3).forEach { foo(it)() }
闭包
// foo 是一个匿名函数的引用,该函数有两个参数
val foo = { x: Int, y: Int -> x + y }
// Java 意义
this.foo = (Function2)null.INSTANCE;
// 两种调用方式
foo.invoke(1, 3)
foo.(1, 3)
// fooo(x: Int) 是后面lambda表达式的引用,所以按照第一种方式比较好理解,就是首先拿到fooo的引用再去invoke他们的方法,不懂,decompile看一下
fun fooo(x: Int) = { y: Int -> x + y }
// Java 翻译
@NotNull
public final Function1 fooo(final int x) {
// 这里可以看到第一个fooo(1) 只到这里,可以得到Function1对象,所谓的闭包也能从这里看出来,如果我们只调用到这里只有一个Function并且包含x的对象,再次调用invoke才能激活使用这个变量,理论上是变相的延长了首次传入x的生命周期
return (Function1)(new Function1() {
// $FF: synthetic method
// $FF: bridge method
public Object invoke(Object var1) {
return this.invoke(((Number)var1).intValue());
}
public final int invoke(int y) {
return x + y;
}
});
}
// 两种调用方式
fooo(1).invoke(2)
fooo(1)(2)
// object形式
fooT(object : AbstractT() {
override fun testMethod() {
}
})
// Java翻译,只是生成一个匿名内部类
(AbstractT)this.fooT.invoke(new AbstractT() {
public void testMethod() {
}
});
在kotlin中,匿名函数体、lambda(以及局部函数、object表达式)在语法上都存在”{}”,由这对花括号包裹的代码如果访问外部环境变量就会被称为一个 闭包。从上面的例子中可以看到,x是外部的变量,fooo就称为一个闭包。
lambda是最常见的一种闭包形式。
隔离性:
表达式有良好的隔离性,就是说通过传入的参数能够更好的使用并且不改变他们的属性和值。
when表达式
when表达式的参数可以省略,这种情况下分支 -> 左侧需要是返回布尔值,否则编译会出错。这个看起来也没啥卵用啊,不用为了省点语句而省,这样语义多不明显。
中缀函数
可以使用infix 关键字以及 to函数 定义中缀函数,中缀函数的使用方式一般为
// a 中缀 b, to 这种形式的定义函数为中缀函数
infix fun <A, B> A.to(that: B): Pair<A, B>
class Person {
infix fun called(name: String) {
println("My name is ${name}")
}
}
// 中缀形式
val p = Person()
p called "test"
// Java 形式
Person p = new Person();
p.called("MMM");
// kotlin 定义
class Person constructor(aaa: String) {
init {
println("ABC")
}
}
// 这里定义的为主函数
class Person constructor(aaa: String) {
// 这里 this(cc) 说明只含有一个String的为主函数,因此init块全部追加到只含有String的构造函数中
constructor(bbb: Int, cc:String) : this(cc){
println(bbb)
}
init {
println("ABC")
}
}
// Java 翻译
public Person(@NotNull String aaa) {
Intrinsics.checkNotNullParameter(aaa, "aaa");
super();
// init 语句块,追加到最后了,init 可以增加很多语句块,会顺序追加到 主构造函数 后面,但是会在从函数前面执行,因为从函数首先调用主函数。
String var2 = "ABC";
boolean var3 = false;
System.out.println(var2);
}
等号定义
两个等号定义值相等,三个等号定义引用相等。
init方法
init 方式的构造方法会在主构造函数的最后执行,如果init 语句块在调用的时候,从函数会首先调用主函数,因此这种情况下 init 语句块在从构造函数之前调用,如果需要永远在最后,那么就需要不再class 函数体哪里定义主函数,定义为统计的函数,这样正确追加。
学到65页,未完待续。。。
20210822 更新
延迟初始化:by lazy 和 lateinit
by lazy 和 lateinit 可以延迟初始化,其中
by lazy 语法特点为:
- 改变量必须是引用不可变的,也就是使用 val 定义,不能使用 var 定义
- 在第一次被使用时才会进行赋值操作
- lazy 背后是接受一个 lambda 并返回一个 Lazy
实例的函数,第一次访问该属性时,会执行 lazy 对应的 lambda 表达式并记录结果,后续访问该属性时只是返回记录的结果
// kotlin 代码
val sex: String by lazy {
if (true) "male" else "female"
}
// 翻译的Java代码
this.sex$delegate = LazyKt.lazy((Function0)null.INSTANCE);
// 这里返回的 Lazy<T> ,默认实现是 Synchronized 形式的实现
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
// 同步方式实现原理
private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
private var initializer: (() -> T)? = initializer
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
// final field is required to enable safe publication of constructed instance
private val lock = lock ?: this
override val value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return _v1 as T
}
return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST") (_v2 as T)
} else {
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}
}
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
private fun writeReplace(): Any = InitializedLazyImpl(value)
}
// 几种模式
public final class LazyKt$WhenMappings {
// $FF: synthetic field
public static final int[] $EnumSwitchMapping$0 = new int[LazyThreadSafetyMode.values().length];
// 1 添加同步锁 2 Publication 参数不加锁 3 None 没有任何线程安全的保证
static {
$EnumSwitchMapping$0[LazyThreadSafetyMode.SYNCHRONIZED.ordinal()] = 1;
$EnumSwitchMapping$0[LazyThreadSafetyMode.PUBLICATION.ordinal()] = 2;
$EnumSwitchMapping$0[LazyThreadSafetyMode.NONE.ordinal()] = 3;
}
}
// Publication模式的实现
private class SafePublicationLazyImpl<out T>(initializer: () -> T) : Lazy<T>, Serializable {
@Volatile private var initializer: (() -> T)? = initializer
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
// this final field is required to enable safe initialization of the constructed instance
private val final: Any = UNINITIALIZED_VALUE
override val value: T
get() {
val value = _value
if (value !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return value as T
}
val initializerValue = initializer
// AtomicReferenceFieldUpdater 使用这个Field Updater 去保证,内部使用了 Updater cas 去比较这个值是否是未初始化的,如果是就更新,否则不动
// if we see null in initializer here, it means that the value is already set by another thread
if (initializerValue != null) {
val newValue = initializerValue()
if (valueUpdater.compareAndSet(this, UNINITIALIZED_VALUE, newValue)) {
initializer = null
return newValue
}
}
@Suppress("UNCHECKED_CAST")
return _value as T
}
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
private fun writeReplace(): Any = InitializedLazyImpl(value)
companion object {
private val valueUpdater = java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater(
SafePublicationLazyImpl::class.java,
Any::class.java,
"_value"
)
}
}
kotlin 关键字
is -> instanceOf
as -> Java 的强转
和类型
A or B
积类型
C and D
协变 和 逆变
out 协变: 这个和Java 的 <? extends T> 语义相同,相当于都是继承于 T 的类型,是 T 的子类型 in 逆变: 这个和 Java 的 <? super T> 语义相同,限定为是 T 的父类,但是 T 的子类也可以,因为 T 的子类明显是可以转变为 T 的
PCES 原则,out 协变和 in 逆变同样支持 PCES 原则, Producer 生产者需要使用 out 协变的类型, Consumer 消费者需要使用 in 逆变类型的泛型。
内联函数
使用 inline 定义内联函数,内联函数执行的时候会把代码编译到使用者内部,这样就可以少一层调用栈。 例如:翻译一下with的定义
// with是内联,其中接收者为 T ,传入的是一个 block 方法,最后返回一个 R 的泛型
inline fun <T, R> with(receiver: T, block: T.() -> R): R
// apply 方法定义,作用到 T 的 apply 方法,调用 T 的 block 方法,最终的返回值是 Unit ,整个 apply 执行之后还是返回 接收者本身
inline fun <T> T.apply(block: T.() -> Unit): T
// T.() 代表调用 T 身上的 lambda 方法
注解定义
注解定义使用 annotatio class 定义注解。 使用 kapt 结合 kotlin 注解可以实现基于元编程的二次编程,基于注解生成类,或者进行拦截定义特定的注解 实现各种功能。
响应式编程和流
Rx 某某某是响应式编程, 流的代表是kotlin的Flow以及Java的Stream,可以不用Rx 方式编程但是响应式编程明显是一个主流。响应式编程是一个思想,就是流驱动程序的运行,收到流或者数据之后进行处理,根据不同的数据做出不同的相应,或者就像我的首页数据解析流程,不同的类型进入不同的 transfer 这样可以最大的灵活性去追求变化。
函数式编程
函数式编程,kotlin 目前不支持纯种的函数式编程,scala 便是纯种的函数式语言,函数式编程的特点是更加数学化或者抽象化,不人类化。因此学习的时候有一定的门槛,主要是思想的转变。
数据类 与 密封类
数据类型用data class 声明,密封类则用 sealed class 声明,其中,data 类一般用于做bean 且他不可继承,sealed 类多用于 when 子句,使用密封类的 when 子句可以没有else 仅仅弄几个继承与 sealed 的类也可。sealed 类只能在一个文件中继承,脱离之后因为他的构造函数是private 的并不能正常继承。
// kotlin
sealed class Xiaoming {
abstract fun foo()
}
data class Xiaomingzizi(val tt: Int) : Xiaoming() {
override fun foo() {
}
}
// Java
// data类默认为final 定义的class 因此不能继承
public final class Xiaomingzizi extends Xiaoming {
private final int tt;
public void foo() {
}
public final int getTt() {
return this.tt;
}
// 这里是调用的有DefaultConstructorMarker的构造方法,因此这里可以继承,外部默认调用的是private 那个因此有问题,使用 sealed 需在一个kt 文件中定义
public Xiaomingzizi(int tt) {
super((DefaultConstructorMarker)null);
this.tt = tt;
}
public final int component1() {
return this.tt;
}
@NotNull
public final Xiaomingzizi copy(int tt) {
return new Xiaomingzizi(tt);
}
// $FF: synthetic method
public static Xiaomingzizi copy$default(Xiaomingzizi var0, int var1, int var2, Object var3) {
if ((var2 & 1) != 0) {
var1 = var0.tt;
}
return var0.copy(var1);
}
@NotNull
public String toString() {
return "Xiaomingzizi(tt=" + this.tt + ")";
}
public int hashCode() {
return this.tt;
}
public boolean equals(@Nullable Object var1) {
if (this != var1) {
if (var1 instanceof Xiaomingzizi) {
Xiaomingzizi var2 = (Xiaomingzizi)var1;
if (this.tt == var2.tt) {
return true;
}
}
return false;
} else {
return true;
}
}
}
// Xiaoming.java
package com.xpj.kotlingrowth;
import kotlin.Metadata;
import kotlin.jvm.internal.DefaultConstructorMarker;
// 默认是 abstract 类定义
public abstract class Xiaoming {
public abstract void foo();
// 这个密封类的构造函数为private
private Xiaoming() {
}
// $FF: synthetic method
public Xiaoming(DefaultConstructorMarker $constructor_marker) {
this();
}
}