天天看点

kotlin 基础总结kotlin 学习总结

kotlin 学习总结

记录一下与java相比的一些基础重要的点

1.基础知识

1.1 基本类型

kotlin中没有java基本类型的int、float、double等,所有东西都是对象,这与java类似。但是kotlin对数字没有隐式拓宽转换,需要显示转换;数字字面量不支持八进制。

1.2 包与导入

使用import关键字,功能上与java差不多。import不限于导入类,还可以导入声明如枚举常量。不同的是 没有类似import static的功能。

1.3 控制流

使用if、when、for、while

when取代了switch功能,但是比switch强大,可以多条件一起,逗号分隔

when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}
           

可以使用任意表达式,而不只是常量;可以检测一个值是否在一个区间

when (x) {
    in 1..10 -> print("x is in the range")
    in validNumbers -> print("x is valid")
    !in 10..20 -> print("x is outside the range")
    else -> print("none of the above")
}
           

for/while的使用跟java有点类似,for循环可以对迭代器对象进行遍历,与in使用。

1.4 返回和跳转

return、continue、break 与java的功能一样,加了标签功能。kotlin的表达式都可以用标签(Label)来标记。标签=标识符[email protected],功能是记录地址,来跳转到相应位置。

1.5 类与对象

1.5.1 类和继承

跟java一样使用class关键字。kotlin的一个可以有一个主构造函数以及一个或者多个次构造函数。语法如下:

lass Person constructor(firstName: String) { ... }
           

如果主构造器没有注解或者可见性修饰符,可以省略constructor关键字

对于类属性的初始化,可以在init关键字的初始化块中,也可以在类属性初始化器初始化。主构造函数的参数可以在初始化块中使用。

1.5.2 属性和字段

类属性,var表示可变,val表示只读,只读属性不允许setter。属性可以自定义getter/setter实现,如下:

val isEmpty: Boolean
    get() = this.size == 0
           

属性类型如果可以从初始化器推断出来(getter返回得到),可以省略。

1.5.3 接口

kotlin的接口作用跟java类似,包含抽象方法的声明也可以包含实现。跟抽象类不同的是,接口不能保存状态。实现接口方式如下:

class Main : MyInterface {
    override fun run() {
        // 方法体
    }
}
           

针对实现多个接口,覆盖冲突的情况:

interface A {
    fun run() { print("A run") }
    fun drink()
}

interface B {
    fun run() { print("B run") }
    fun eat() { print("B eat") }
}

class C : A {
    override fun run() { print("C run") }
}

class D : A, B {
    override fun run() {
        super<A>.run()
        super<B>.run()
    }

    override fun eat() {
        super<B>.eat()
    }
}
           

需要指明如何实现继承的接口方法。

1.5.4 可见修饰符

相比java,kotlin 在java常用的public、protected、private中,加了一个internal。函数、属性和类、对象、接口都可以在顶层声明。

对于包内:

可见修饰符 说明
public 如果不指定任何可见修饰符,默认为public
private 在声明的文件内可见
protected 不适用于顶层说明
internal 在相同模块内可见,模块就是一个idea模块、maven项目、Ant任务执行编译的一套文件、一个Gradle源集。

对于类内部声明的成员:

可见修饰符 说明
private 在这个类内部可见
protected 当前类以及子类可见
internal 类声明的本模块内可见
public 外部都可以见

kotlin中外部类不能范文内部类的私有成员。

1.5.5 数据类

数据类不能是抽象、开放、密封、或者内部的。

数据类的主构函数的参数至少一个。声明如下所示:

data class User(
    val name: String,
    val gender: String,
    val age: Int
){
    fun validate(): Boolean {
        return true
    }
}
           

var/val必须要带上,编译器为了吧主构函数中声明的所有属性,自动生成下面的函数:

  • equals()/hashCode()
  • toString() : 格式是 User(name=Jacky, gender=Male, age=10)
  • componentN() 函数 : 按声明顺序对应于所有属性component1()、component2() …
  • copy() 函数

数据类替换普通类的好处,可以节省代码,省去一些工作:

(1)在构造函数中声明属性(非数据类特有),除了构造函数,避免所有getter/setter代码

(2) euquals() /hashcode()

(3) Copy() 方法,在使用不变对象时可以使用

1.5.6 泛型

kotlin 没有类型通配符。针对泛型,kotlin有型变,声明处型变与类型投影。

(1)声明处型变

out修饰符:

interface Source<out T> {
    fun nextT(): T
}
fun demo(strs: Source<String>) {
    val objects: Source<Any> = strs //T 是一个 out-参数
}
           

in修饰符号:

interface Comparable<in T> {
    operator fun compareTo(other: T): Int
}
fun demo(x: Comparable<Number>) {
    x.compareTo(1.0) // 1.0 拥有类型Double,是Number的子类型
    //可以将 x 赋给类型为 Comparable<Double>的变量
    val y: Comparable<Double> = x
}
           

(2)类型投影

与java原声类型比较类似,为了可以安全使用。安全使用是指对泛型类型定义一个类型投射, 要求这个泛型类型的所有的实体实例, 都是这个投射的子类型。

1.5.7 拓展函数

Kotlin 支持拓展函数和拓展属性。

声明一个扩展函数,需要用一个接收者类型即被扩展的类型来作为他的前缀。如为MutableList 添加一个swap 函数:

fun MutableList<Int>.swap(index1: Int, index2: Int) {
    val tmp = this[index1] // "this"对应该列表
    this[index1] = this[index2]
    this[index2] = tmp
}
           

1.5.8 单例(Singleton) 与 伴生对象(companion object)

kotlin 没有静态属性和方法,object声明的对象可以提供类似单例的功能。

object Student{
   val studentId:Int?=null
   val username:String?=null
}
           

对象属性通过对象名访问。

kotlin提供伴生对象,使用companion object 关键字声明,如

class DataProcessor {
  fun process() {
        println("Process Data")
  }
   object FileUtils {
         val userHome = "/Users/jack/"
         fun getFileContent(file: String): String {
         var content = ""
         val f = File(file)
         f.forEachLine { content = content + it + "\n" }
         return content
     }
   }
   companion object StringUtils {
          fun isEmpty(s: String): Boolean {
          return s.isEmpty()
     }
  }
}
           

一个类只能有一个伴生对象,默认引用名为Companion。使用这个实现同java静态类使用静态方法的功能.没有制定伴生对象名称时,调用属性或者方法时,Companion可以省略不写。

伴生对象的初始化是在相应类被加载解析的时候,这个与java静态初始化类似。伴生对象的成员很像java以及其他语言的静态成员,但是运行时任然是一个对象的实例成员,还可以实现接口,如:

interface BeanFactory<T> {
    fun create(): T
}
class MyClass {
    companion object : BeanFactory<MyClass> {
        override fun create(): MyClass {
        println("MyClass Created!")
        return MyClass()
        }
    }
}

           

使用java中的静态成员和静态方法还可以使用注解

@jvmField:生成与该属性相同的静态字段

@JvmStatic: 在单例对象和伴生对象中生成静态方法

1.5.9 委托

(1) 代理模式

代理模式中,两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。代理模式可以用

聚合替代继承。

(2) 委托属性

class DeletgatePropertiesDemo{
    var content: String by Content()
    override fun toString(){
       return " seewo"
    }
}

class Content{
     operator fun getValue(){
         return "live"
     }
     operator fun setValue(value:String){
        println("$value")
     }
}
           

使用by 将属性的getter、setter委托给Content对象,对象的对应的两个函数,而且函数是操作符函数。

懒加载属性委托:

val synchronizedLazyImpl = lazy({
    println("seewo 3!")
    println("seewo 2!")
    println("seewo 1!")
    "Hello 1! "
})
val lazyValueSynchronized1: String by synchronizedLazyImpl
println(lazyValueSynchronized1)
println(lazyValueSynchronized1)
val lazyValueSynchronized2: String by lazy {
    println("seewo 3!")
    println("seewo 2!")
    println("seewo 1!")
    "Hello 2!"
}
println(lazyValueSynchronized2)
println(lazyValueSynchronized2)

           

2.其他

2.1 字符串模板

字符串包含模板表达式,会进行求值,并将结果合并到字符串中,比较简洁好用,跟使用log库打印日志的时候使用模板类似。如

val i=10
val s="i=$i"
           

2.2 空安全

kotlin能够规避空指针,在调用属性时候安全调用,可以根据如下方式:

(1) 在条件中检查null,例如

val l = if (b != null) b.length else -1
           

(2) 使用安全调用操作符 ?. 例如:

bob?.department?.head?.name
           

(3) Elvis 操作符 ?:

val l = b?.length ?: -1
           

(4) !!操作符

val l = b!!.length
           

用过GitLab拉取的kotlin项目看,比较常用这种。

2.3 kotlin集合

kotlin集合区分可变以及不可变。

kotlin对list、set等是通过库函数创建如:

ListOf()、mutableListOf()、SetOf()。对与map操作使用的库命令与List、Set类似,但是map分多种,有TreeMap,HashMap。

底层的实现原理与java类似。开发中,习惯使用java 集合库的,使用Guava API,通过使用一些常用API,提供集合的创建、使用方式跟Java很类似,容易上手。

2.4 异常

kotlin所有异常类都是Throwable类的子类,异常有堆栈回溯信息、原因等信息。主要的使用方式也是throw抛异常,用try-catch捕获异常。不同的是kotlin没有受检异常。

2.5 协程

1.2 版本的kotlin提供的协程机制处于实验阶段,自搭的kotlin环境也是1.2版本,api提示不稳定,不能用于生产环境,1.3版本官方说提供了稳定的API。

(1) 协程个人简单理解

程序包含主进程,主进程可能包含多个线程,线程包含多个协程,协程之间可以嵌套。严格来说其实协程可以直接运行在进程中,非一定依赖线程。但是目前支持协程的语言如kotlin、Golang、Python大都基于主线程开启程序的运行。不同语言的协程实现方式大同小异,作用效果相同。

(2) 协程的应用场景

如遇到线程阻塞任务时,开启协程,可以减少资源消耗(协程资源消耗比线程少,主要通过提升CPU利用率,减少线程间的切换来提升程序运行效率)

(3) 特性

官方说法简述:

  • 可控制:协程能够被控制的发起子任务
  • 轻量级:协程占用资源比线程小
  • 语法糖:多任务或多线程切换不使用回调语法