Kotlin 语法回顾

111 阅读2分钟

运算符

运算符对应方法
+aa.unaryPlus()
-aa.unaryMinus()
!aa.not()
a++a.inc()
a--a.dec()
a+ba.plus(b)
a-ba.minus(b)
a*ba.times(b)
a/ba.div(b)
a%ba.rem(b)
a..b 闭区间a.rangeTo(b) 闭区间
a in ba.contains(b)
a !in b!b.contains(a)
a[i]a.get(i)
a[i]=ba.set(i,b)
a+=ba.plusAssign(b)
a-=ba.minusAssign(b)
a*=ba.timesAssign(b)
a/=ba.divAssign(b)
a%=ba.remAssign(b)
a==ba?.equals(b)?:(b===null)
a!=b!(a?.equals(b)?:(b===null))
a>ba.compareTo(b)>0
a<ba.compareTo(b)<0
a>=ba.compareTo(b)>=0
a<=ba.compareTo(b)<=0

注意1:使用a++和++a和Java中是一致的,再使用对应方法时不太相同,使用时注意,优先使用++

注意2:可以使用in 来判断一个字符串是否包含另一个字符串

**注意3:kotlin中的==相当于是空安全的equals,所以kotlin中引用比较使用===和!==是和java中==和!=是等效的

注意4:在java中只要实现了Comparable接口,就可以通过kotlin中的>,<进行比较,例如String,Date

位运算符(只能对Int和Long起作用)

  • and 按位与
  • or 按位或
  • inv 按位非
  • xor 按位异或
  • shl 左移运算符
  • shr 右移运算符
  • ushr 无符号右移运算符

区间运算符

  • a until b半开区间
  • a downTo b反向闭区间
  • a..b 正向闭区间
  • step 步长

运算符重载

    语法:
    1 在对应类中(往往是可以直接修改源码) operator fun 函数名称([参数]):返回值{}
    2 不能直接修改源码的时候,使用扩展方法的方式 operator fun 类名.函数名([参数]):返回值{}

流程控制

if... else ...

注意:在使 if else 语句时,一定要先处理包含范围史小的情况。 在使 if se 语句时,一定要先处理包含范围史小的情况。

for..in.. 注意:for...in...中相当于一个用val声明的常量,所以不能从新进行赋值

忽略外层某次循环的语法

outer@ for (i in 0 until 5){
    for(j in 0 until 3){
    if(j==1){
        continue@outer
    }
    }
}

return

需要注意的是,无论在多少层循环的内部,一旦遇到return,就会直接终止方法或者函数的执行

数组和集合

  • indicies 获取数组索引区间来代替 0 until array.size这种方式来代。

  • find和findLast: 注意:这个其实是调用的firstOrNull和lastOrNull,查找的从前往后第一个和从后往前一个符合条件的元素

  • setOf 和 mutableSetOf返回的是LinkedHashSet ,hashSet返回HashSet是无序的,sortedSet 返回TreeSet从小到大排序,这个是java三种Set的特性决定的。

  • Set 中 intersect/union 是取两个Set的交集和并集

  • mapOf和mutableMapOf返回的是LinkedHashMap,hashMap返回的HashMap是不维护顺序的,linkedHashMap返回的LinkedHashMap按照添加顺序排列,sortedMapOf返回的TreeMap是按照key从小到大排列

函数和Lambda表达式

  • 尾递归函数:关键字tailrec修饰 tailrec fun factRec(n: Int, total : Int= 1): Int =

if (n == 1) total else factRec(n - 1 , total * n)

  • 高阶函数 双冒号::可以将函数的引用赋给变量

  • 局部函数和Lambda 注意点:函数的最后一个表达式自动被当做Lambda表达式的返回值,无须使return关键字 Lambda表达式无法返回指定类型

  • 匿名函数和Lambda表达式return


fun main() {

    val data = arrayOf(1, 2, 3, 4, 5, 6)
    /**
     * Lambda中直接使用return会导致直接返回到Lambda所在的函数中,此例子中是直接返回main函数
     */
    data.forEach {
        println(it)
        return
    }
    /**
     * Lambda可以使用return@forEach返回Lambda表达式
     */
    data.forEach {
        println(it)
        return@forEach
    }
    /**
     * Lambda可在之前加上  Lambda名字@ 来给Lambda命名
     */
    data.forEach repeat@{
        println(it)
        return@repeat
    }

    data.forEach(
        fun(i) {
            println(i)
            //匿名函数中的 return 用于返回该函数本身
            return
        }
    )
}

面向对象

internal 修饰符表示包内可见

中缀表示法

  • infix 修饰函数,并且函数只有一个参数
fun main() {
    val w1 = Weight(2.2)
    val w2 = Weight(3.0)
    val w3 = w1 add w2
    println(w3.weight)

}

data class Weight(var weight: Double = 0.0) {
    infix fun add(weight: Weight): Weight {
        return Weight(this.weight + weight.weight)
    }
}

componentN,解构 和 setter,getter

class Student {
    var name: String? = null
        set(name) {
            field = name?.reversed()

        }
        get() {
            println("调用get()")
            return field
        }
    var age: Int = 0

    operator fun component1(): String? {
        return name
    }

    operator fun component2(): Int {
        return age
    }
    
}


main函数调用

val (name, age) = student

println(name)
println(age)

幕后字段

setter和getter中需要使用幕后字段field对属性进行操作

幕后属性

// 这种写法 _age为幕后属性,这样写很复杂,一般不需要这么写
private var _age: Int = age
var age: Int
    private set(value) {}
    get() = _age

lateinit

  1. 可变属性
  2. 不能有自定义setter,getter
  3. 必须是非空的
  4. 不能是原生类型(java的8种基本类型)
  5. 访问之前需要赋值

内联属性

在 getter, setter,属性之前使用inline修饰,表示调用setter和getter会内联化

import

import ... as ... 导包指定别名

java 默认访问控制符:

default class 类名

kotlin 默认访问控制符
public final class 类名

kotlin 取消了java的default(包访问权限),引入了internal访问控制符(模块访问权限) Kotlin取消了protected 的包访问权限

kotlin open 属性重写如果不修改访问权限,会保持重写之前的访问权限

深入构造器

主次构造器都有的情况下 次构造器需要委托init初始化块

    语法:
    constructor(构造参数列表):this(构造参数列表)

重写父类属性

注意点:重写的属性的修饰符权限要大于等于父类属性的修饰符权限, public >private, var>val

class WashingMachine : Machine() {
    override var name: String = "子类属性"
}

open class Machine {
    open val name: String = "父类属性"
}

强制重写

当继承了两个类,接口,抽象类中有同名方法,则在子类中必须重写这个重名方法,并且可以通过super<类名>.方法名()选择性的去确定要调用哪些类中的父类

as

  • as 类型强转,类型不匹配引发异常
  • as?类型不匹配会返回null

内部类和嵌套类

Kotlin中的嵌套类相当于java中的静态内部类,Kotlin中的内部类相当于java中的非静态内部类。

扩展

扩展函数

fun 类.方法名():返回值{}

扩展属性

var 类名.属性名
set(value){
}
get(){}

注意点:

  1. 扩展属性只能对已有属性进行计算,无法真正的添加随意的属性,因为扩展属性没有field字段
  2. 需要提供setter和getter
  3. 不能设置初始值

为类扩展匿名函数(Jectpack 中大量使用了这种方式)

val plusOne=fun Int.():Int{
    return this+1
}
val b= a.plusOne()
println(b)

Jectpack Compose 伪代码


class Column(alignment: Int, lambda: Column.() -> Unit)

class Text(text: String)

class Image(img: String)


Column(alignment = 1) {
    Text(text = "")
    Image(img = "")
}

final 和 open

与java不同的是,kotlin 局部变量不能使用final和open进行修饰

image.png孔int'l孔天林

密封类

异常

Kotlin中异常处理基本与java一致 注意点:在包含多个catch块的时候,应该先catch小异常,再catch大异常,跟if...else... 条件判断一致,先判断小条件,再判断大范围

        try {
//            1 / 0  ArithmeticException
//            "2-".toInt() NumberFormatException
//            println((null as String?)!!.length) NullPointerException
        } catch (e: ArithmeticException) {
            e.printStackTrace()
        } catch (e: NumberFormatException) {
            e.printStackTrace()
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            print("finally 结束")
        }
    }

自定义异常

class AmountLowException(
    message: String? = null, cause: Throwable? = null
) : Exception(message, cause)

@Test
fun throwException() {
    fun pay(amount: Double) {
        if (amount < 100) {
            throw AmountLowException(message = "金额少于100,无法进行充值")
        }
        print("充值成功")
    }
    pay(90.0)
}