Kotlin 机制你都了解吗?

2021-04-07 15:31:07 浏览数 (1)

1. 什么是 kotlin?


kotlin 是静态类型的编程语言,运行于 jvm 之上。如果在编译时知道变量的类型,则语言是静态类型的,在运行时知道变量类型,则语言是动态类型。

2. 什么是 extension(扩展)函数


Kotlin 可以对一个类的属性和方法进行扩展,对被扩展的类代码本身不会造成任何影响。 扩展函数可以为已经存在的类添加新的方法,并且不会修改原来的类。

代码语言:javascript复制
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

       User("libo", 10).login()
    }

    fun User.login() {
        Log.i("TAG","去登录")
    }
}

data class User(var name: String, var age: Int)

3. 在 kotlin 中有多少种构造函数


kotlin 将构造函数分为了两种: 主构造函数次构造函数

代码语言:javascript复制
//主构造方法如下,跟在类名后面
class Person constructor(name:String){

}
//无参主构造方法
class Person constructor(){

}
//当主构造方法没有任何注解或者可见性修饰符时,可以省略,写成下面这样
class Person {

}
代码语言:javascript复制
class Person {

    /**
     * 无参次构造方法
     */
    constructor(){

    }
    /**
     * 有参次构造方法
     */
    constructor(name:String){

    }
}

主构造函数没有函数体,如果我们想在主构造函数中写一些逻辑,怎么办呢, kotlin 给我们提供一个 init 结构体,所有构造函数中的逻辑都可以写在里面:

代码语言:javascript复制
class Person(val name: String, val age: Int) : Person() {
    init {
        println("name is $name")
        println("age is $age")
    }
}

4. lateinit 和 by lazy 的区别


Kotlin 基于 Java 的空指针提出了一个空安全的概念,即每个属性默认不可为 null。在某个类中,如果某些成员变量没办法在一开始就初始化,并且又不想使用可空类型(也就是带?的类型)。那么,可以使用 lateinit 或者 by lazy 来修饰它。

  1. lateinit 只能用于修饰变量 var,不能用于可空的属性和 Java 的基本类型。
  2. lateinit 可以在任何位置初始化并且可以初始化多次。
  3. lazy 只能用于修饰常量 val,并且 lazy 是线程安全的。
  4. lazy 在第一次被调用时就被初始化,以后调用该属性会返回之前的结果。

5. let, apply, run, with 的区别


6. 高阶函数


如果一个函数接收另一个函数作为参数,或者返回类型是一个函数,那么这个函数我们就称之为高阶函数。

举例如下,这些函数用法与 Rxjava 同名函数类似

代码语言:javascript复制
//forEach,用于遍历集合
val arr = intArrayOf(1, 2, 4, 6)
arr.forEach {
    println(it)
}

 //map 返回一个每一个元素根据给定的函数转换所组成的List
val arr = intArrayOf(1, 2, 4, 6)
val newArr = arr.map { (it * 2).toString()  }
println(newArr)

//flatMap  遍历所有的元素 ,为每一个创建一个集合 ,最后把所有的集合放在一个集合中
val arr = intArrayOf(1, 2, 4, 6)
val arr2 = intArrayOf(10, 39, 39, 18, 88)
var arr3 = intArrayOf(100, 200, 383, 198)
arrayListOf(arr, arr2, arr3).flatMap { iterator ->
    iterator.map {
        println(it.toString())
    }
}

//filter,用于过滤数据
val arr = intArrayOf(1, 2, 7, 6, 10, 39, 39, 18, 88)
arr.filter {
    //这里是通过条件
    it % 2 == 0
}

//takeWhile,带满足条件的过滤
//它的实现和filter不同地方在filter总是会遍历当前IntArray的所有元素,而takeWhile在第一次发现元素不满足的时候就不再遍历
val arr = intArrayOf(1, 2, 4, 6, 8, 9, 10, 12, 14)
arr.takeWhile {
    it % 2 == 0
}

//take/takeLast
val arr = intArrayOf(1, 2, 4, 6, 8, 9, 10, 12, 14)
var list = arr.take(5)  //取前5个
var list2 = arr.takeLast(3)  //取后3个

7. 伴生对象的总结


类似于 Java 中使用类访问静态成员的语法。因为 Kotlin 取消了 static 关键字,所以 Kotlin 引入伴生对象来弥补没有静态成员的不足。可见,伴生对象的主要作用就是为其所在的外部类模拟静态成员。

注意:

  • 每个类可以最多有一个半生对象;
  • 使用 const 关键字修饰常量,类似于 Java 中的 static final修饰。
  • 可以使用 @JvmField 和 @JvmStatic 类似于 Java 中调用静态属性和静态方法;
  • 伴生对象可以扩展属性和扩展方法。

8. init 代码块和构造方法以及伴生对象中代码的调用时机


创建 Person 类,创建 person 对象打印方法调用时机:

代码语言:javascript复制
class Person {
    private var name: String = "jack"
    constructor() {
        println("constructor 方法调用")
    }
    init {
        println("init 方法调用")
    }
    companion object {
        init {
            println("companion init 1")
        }
    }
}

从 Tools-->kotlin-->show Kotlin Bytecode,将 Person 类反编译成 java 类得到:

伴生对象转为了静态代码块,init 代码块插入到了构造方法的开头处。静态代码块在编译期运行,然后依次运行构造方法的代码。打印的结构为:

结论:伴生对象先于init方法,再先于构造方法。首先伴生对象中的代码是在类加载时就会执行。init代码块中的方法会按顺序放在主构造函数中,主构造函数中原来的代码会在后面执行。

9. const 和 val 有什么区别?


所述 const 关键字被用于声明那些不可变在本质即,这些属性是只读属性的属性。 但是,这些属性的值必须仅在编译时已知,这 const 就是也称为编译时常量的原因。相当于 java 中的 static final 修饰。该val关键字还用于只读属性。但是 const 和之间的主要区别在于 val,val 属性也可以在运行时进行初始化,即不可变变量。

10. Kotlin data 类机制


创建 DataBean 类:

代码语言:javascript复制
class DataBean(var uId: Int, val phoneNum: String, val address: String)

转成 java 类,知道为什么 Kotlin 开发强大了吧。

代码语言:javascript复制
public final class DataBean {
   private int uId;
   @NotNull
   private final String phoneNum;
   @NotNull
   private final String address;

   public final int getUId() {
      return this.uId;
   }

   public final void setUId(int var1) {
      this.uId = var1;
   }

   @NotNull
   public final String getPhoneNum() {
      return this.phoneNum;
   }

   @NotNull
   public final String getAddress() {
      return this.address;
   }

   public DataBean(int uId, @NotNull String phoneNum, @NotNull String address) {
      Intrinsics.checkNotNullParameter(phoneNum, "phoneNum");
      Intrinsics.checkNotNullParameter(address, "address");
      super();
      this.uId = uId;
      this.phoneNum = phoneNum;
      this.address = address;
   }
}

反编译成 java 代码后自动生成了变量的 get、set 方法,equals 方法,copy 方法,hashCode() 方法。如果这些函数中的任何一个在类体中显式定义或继承自其基类,则不会自动生成该函数。如果变量是 val 修饰,只会生成 get 方法。

11. 什么是 Range 操作符?


Range 是 Kotlin 相对 Java 新增的一种表达式,它表示的是值的范围,类似于数学中的区间。 Range 的表达式是像这样子的:1..20,其中..是运算符,它表示一个闭区间 [1, 20]。

0 人点赞