Kotlin知识归纳(一) —— 基础语法

3,653 阅读9分钟

前序

        在19年的Google I/O大会上,Kotlin 成为 Android 开发首选语言。而著名的OkHttp 已经开始用 Kotlin 进行重写工作。是时候通过写博客归纳来巩固Kotlin基础知识。

(一)、语法上的变更

  • 创建对象不需要new关键字
  • 语句不需要;结尾,加;也无所谓
  • 变量类型后置,即变量名在前,变量类型在后。例如 str:String
  • 使用 println() 替代 System.out.println()

(二)、定义变量

        Kotlin中使用val关键字声明常量(即变量初始化之后不可再次赋值),var关键字声明变量。

变量定义时可以不显示指定类型,编译器会根据其初始化器的类型进行推断。

//自动推断出 `Int` 类型
var daqi = 1
//可以显式地指定变量的类型
val a :String = "daqi"
//如果变量没有初始化器,需要显式地指定它的类型
val c: Int
c = 3  

当编译器能确保val变量只有唯一一条初始化语句会被执行,可以根据条件对它初始化不同的值。

val daqi:String
var isChange = true
if (isChange){
   daqi = "1"
}else{
   daqi = "2"
}

val变量的引用自身是不可变的,但是它指向的对象是可变的。

val languages = arrayListOf("Java")
languages.add("Kotlin")

var 关键字允许变量改变自己的值,但不能改变自己的类型。

var daqi = 1
//编译不通过
daqi = ""

(三)、定义函数 Kotlin 中使用 fun 关键字声明函数。 完整的函数定义:

修饰符(默认public)+ fun + 方法名 + (参数列表) :返回值 {
        函数体
}

public fun daqi(name:String):String {
}

表达式函数体

        当单个表达式构成完整的函数体时,可以直接去掉 {} 和 return 语句,函数的返回值类型编译器也会自动推断。这种函数就是表达式函数。

fun sum(a: Int, b: Int) = a + b

语句和表达式的区别在于:

  • 表达式有值,并且能作为另一个表达式的一部分使用;
  • 语句总是包围着它的代码块中的顶层元素,并且没有自己的值。

        在 Java 中,所有的控制结构都是语句。而在 Kotlin 中,除了循环( for, do 和 do/while )以外大多数控制结构都是表达式(例如:if、 when 以及 try 属于表达式)

         表达式函数不光用在些简单的单行函数中 ,也可以用在对更复杂的单个表达式求值的函数中。

fun max(a: Int, b: Int ) = if (a > b) a else b

无返回值的函数

类似Java的返回值类型为void的函数

fun daqi():Unit{
}

可省略Unit类型

fun daqi(){
}

(三)、字符串模板

在 Java 中,当需要打印变量描述和其值时,往往如下打印:

String str = "daqi";
System.out.println("str = " + str);

    Kotlin支持字符串模板,可以在字符串中引用局部变量,但需要在变量名前加上$。表达式会进行静态检查, 如果$引用一个不存在的变量,代码不会编译通过。

val str = "daqi"
printlin("str = $str")

$不仅限于引用于简单的变量名称,还可以引用更复杂的表达式。

val daqi = intArrayOf(1,2,3)
println("${daqi[0]}")

在$引用的表达式内(即花括号{}中)可以直接嵌套双引号

println("daqi的第一个元素: ${if(daqi.size > 0) daqi[0] else "null"}")

当需要打印$符号时,可以对其进行转义,或者利用表达式对其进行打印:

//打印结果为:$str
val str = "daqi"
println("\$str")
println(${'$'}daqi)

(四)、类和属性

在java中定义一个简单类:

public class Person{
    private final String name;
    
    public Person(String name){
        this.name = name;
    }
    
    public String getName(){
    	return name;
    }
}

用Kotlin写的Person类(默认public修饰)

class Person(val name:String)

延伸:若想知道Kotlin对应的具体Java实现,可以通过idea的工具,对Kotlin文件进行反编译:

    Tools ->Kotlin -> Show Kotlin Bytecode -> 右侧弹出字节码框 ->左上角 Decompile按钮

整体基本和Java类相似,但是该类被定义为final类,即太监类,不可被继承。

setter 和 getter

    在Kotlin中,当你声明属性的时候,也就声明了对应的访问器(即get和set)

    val属性只有一个getter,var属性既有 getter 和 setter。 声明一个属性的完整语法:

var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]

    其中,初始化器,getter 和 setter 都是可选的。如果类型可以从初始化器或者getter 返回值中推断出来,也可以省略。 访问器的默认实现非常简单,即对对应变量的返回和更改。

当需要在值被访问或修改时提供额外的逻辑,可以通过自定义getter和setter

自定义getter

在Kotlin中的实现

class Rectangle(val height:Int,val width:Int){
    val isSquare:Boolean
        get(){
        	return height == width
    	}
}

对应的Java实现:

自定义setter

在Kotlin中实现:

class Rectangle(val height:Int,val width:Int){
    var isSquare:Boolean = false
        set(value) {
            field = value
        }
}

在setter方法中,使用特殊的标识符field来访问支持字段(幕后字段)的值。具体幕后字段后面再说。

注意:

    Kolin中,名称以is开头的变量。转换成对应的Java get 和 set 方法时,getter不会添加任何的前准,setter名称中的is会被替换成set。

class Person(
    var isDaqi:Boolean = false
)

该对象在Java中的访问器为:

(五)、迭代

Kotlin中 while 和 do-while循环,其语法与Java的基本没区别。

而for循环仅以一种形式存在,和for-each差不多。但用 in 取代了 :

fun iterationArray(args:Array<String>){
    for (str in args) {

    }
}

    for循环还可以遍历区间。区间本质上是两个值之间的间隔。使用..运算符表示区间。

    Kotlin中的区间是闭合区间,即结束值也是区间的一部分。而区间默认迭代步长为1。

val oneToTen = 1..10

    可以通过step关键字修改步长。遍历时,i变量其增长幅度将变为2,即每次迭代时都会自增2。

for(i in 0..100 step 2){
}

如果想要一个半闭合区间,即结束值不属于区间的一部分,可以使用until关键字,

val oneToNine = 1 until 10

如果需要一个倒序的区间,可以使用downTo关键字

val tenToOne = 10 downTo 1

此时,将从10一直递减到1进行循环。downTo表示的区间也是一个闭合区间。

如果想实现普通for循环一样,对数据索引进行循环,可以使用数组的indices变量

for (i in args.indices) {
    println(args[i])
}

(六)、if表达式

在Kotlin中,if是一个表达式,即它会返回一个值。

if的分支为代码块时,最后的表达式将作为该代码块的值:

val max = if (a > b) {
    print("Choose a")
    a
} else {
    print("Choose b")
    b
}

(七)、when表达式

Kotlin中的when,对应的是Java中的switch,但比起更加强大。

    when 将它的参数与所有的分支条件按顺序比较,直到某个分支满足条件,执行其分支对应的代码。当多分支需要用相同的方式处理时,可以把其放在一起,用逗号分隔。

    当when作为表达式使用时,必须有 else 分支, 除非编译器检测出所有的可能情况都已经被覆盖。(例如枚举类)

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

有没有发现,没有了那繁琐的case和break!!

    when的参数可有可无,并且无限制类型!而且可以用任意表达式作为分支条件。(switch要求必须使用常量作为分支条件~)

    when作为表达式函数的的函数体,直接使用函数的参数,自身并无设置参数。并在条件语句中使用in或者!in 判断一个变量是否在区间或者集合中。

fun daqi(num:Int,intArray: IntArray) = when{
    num in 1..10 -> "1~10"
    num !in intArray -> "no in Array"
    else -> "other"
}

when 也可以用来取代 if-else if链.

fun daqi(num:Int) = when{
    num < 0 -> "小于0"
    num >0 && num < 10 -> "1..10"
    else -> "other"
}

    is可以检查一个变量是否是某种类型,再配合智能转换(检查过某个变量的类型后,不再需要再转换它,直接可以把它当作检查过的类型使用)可以很方便的对进行类型安全操作。

//Any类似于Java的Object
fun daqi(num:Any) = when(num){
    is String -> {
        //此时num已经为String类型
        println("该变量类型为String,获取其长度")
        println(num.length)
    }
    is Int -> {
        //此时num已经为Int类型
        println("该变量类型为Int,将其与10进行对比")
            num.rangeTo(10)
    }
    else -> "other"
}

(八)、Kotlin中的异常

    Kotlin的异常处理与Java类似。当抛出异常时,不需要使用new关键字创建异常实例。

var daqi:String? = null
if (daqi == null){
    throw NullPointerException("daqi is null")
}

    try也可以作为表达式,将其赋值给变量。当代码执行正常,则try代码块中最后一个表达式作为结果,如果捕获到异常,对应catch代码块中最后一个表达式作为结果。finally 块中的内容不会影响表达式的结果。

fun readNumber(reader:BufferedReader):Int? = try{
    Integer.parseInt(reader.readLine())
}catch(e:NumberFormatException){
    null
}catch (e:NullPointerException){
    null
}finally {
    reader.close()
}

可以有0到多个 catch 块。finally 块可以省略。 但是 catch 与 finally 块至少应该存在一个。

(九)、枚举

    kotlin中用两个关键字enum和class声明枚举类。enum是一个软关键字,只有当它出现在class前面时才有特殊的意义。

声明普通枚举类:

enum class Color{
    RED,BLUE
}

声明带属性的枚举类:

enum class Color(val r:Int,val g:Int,val b:Int){
    RED(255,0,0),BLUE(0,0,255);
    fun rgb = (r * 256 + g) * 256 + b
}

如果在枚举类型中定义任何方法,需要使用分号;把枚举常量列表和方法定义分开。

参考资料:

android Kotlin系列:

Kotlin知识归纳(一) —— 基础语法

Kotlin知识归纳(二) —— 让函数更好调用

Kotlin知识归纳(三) —— 顶层成员与扩展

Kotlin知识归纳(四) —— 接口和类

Kotlin知识归纳(五) —— Lambda

Kotlin知识归纳(六) —— 类型系统

Kotlin知识归纳(七) —— 集合

Kotlin知识归纳(八) —— 序列

Kotlin知识归纳(九) —— 约定

Kotlin知识归纳(十) —— 委托

Kotlin知识归纳(十一) —— 高阶函数

Kotlin知识归纳(十二) —— 泛型

Kotlin知识归纳(十三) —— 注解

Kotlin知识归纳(十四) —— 反射