阅读 100

Kotlin 总结系列(1) —— 基础要素

kotlin学习总结系列,主要是自己的学习笔记,主要参考自Kotlin 实战Kotlin官网如有错误或者可以改进的地方,望指出赐教。不积跬步无以至千里,Android知识的庞大,自身知识的不完善,须不断向大佬学习。

这一部分主要记录如何开始使用kotlin现有库和API,如控制结构、函数声明、方法声明,伴生对象和lambda等

一、 kotlin的一些特性

  1. kotlin是针对java平台的新编程语言(也是基于jvm),能与java无缝对接。同时有大量实用语法糖帮助我们快速构建代码。
  2. kotlin也是一种静态类型的编程语言,在kotlin中很多情况下可以不显式声明变量的类型,可以根据上下文自动判断,即类型推导。如: val name = "Kotlin",可推断出name属于字符串类型
  3. kotlin是偏向于安全式编程的,如可以有效避免NullPointerException(通过可空类型和一系列可空操作避免,后续会详解);和ClassCastException,kotlin将检查和转换组合成一次操作。如:
if(value is String){          //检查类型
    println( value.toUpperCase() )       //通过则直接转换为改类型
}
复制代码
  1. 在kotlin中,可以把多个类放在同一个文件中,文件的名字还可以随意选择,函数也可以放在类外面。
  2. if、when、try,throw等,都是表达式,而不是语句(后面会有例子)
  3. 创建实例时,不用new ,且不用以 ";" 结尾

二、 基本要素:变量,函数,类和枚举

简要说明基本要素的声明和使用

1 变量

kotlin中,变量声明以关键字开始,然后是变量名,最后是类型(可以不加),如:(创建变量时,不用写明 new)

var i:Int = 24 //显式指明类型 
val persion = Persion() //不用 new
val name = "Cap"//省略类型
val age :Int //如果还未初始化,必须显式指明类型,否则无法推导
age = 24
复制代码

kotlin中,变量声明关键字只有两个:val 和 var。 其中,val是不可变引用,相当于java的final声明;而var则是可变引用相当于java的非final,即普通变量

2 字符串模板

kotlin的字符串,像大多数脚本语言一样,如Groovy,可以引用局部变量表达式( 在美元符号$后紧跟表达式 ),使结构更紧凑,如:

val args = arrayOf("John","Ethan")
val name ="index1:$args[0]"  //简单表达式
val more = "Hello,${if (args.size>0)args[0]else "a"}" //更复杂表达式
println("THis is \$x") //打印结果是 THis is $x,打印$字符时,需要转义
复制代码

3 函数

函数分为代码块函数体,和表达式函数体,如下:

(1)普通函数声明,即代码块函数声明,其中 a,b是参数,:Int是指函数返回类型

fun max(a:Int,b:Int):Int{
    return if (a>b)a else b //if 是表达式而不是语句,可以直接代表一个值
}
复制代码

(2)表达式函数体,如上面的例子,是由单个表达式构成的,可以去掉花括号和return语句,如:

fun max(a:Int,b:Int)=return if (a>b)a else b 
复制代码

注: 对于代码块函数体,必须显式写出返回类型和return语句

4 类

kotlin中函数的声明,可以省略大量重复性代码,尤其是对于bean类型。如,

class Rectangle(val width:Int,val height:Int)
复制代码

与以下java定义完全相同:

public class Rectangle {
    private final int width;
    private final int height;
    
    public Rectangle(int width,int height){
        this.width = width;
        this.height = height;
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }
}
复制代码

仔细观察,kotlin的实现是少了public修饰符的,这是因为在kotlin中,public是默认修饰符,关于更多的修饰符和可见性等,后续会详解

在kotlin中,类属性的声明与声明一个变量一样,使用 val 和 var 关键字,其中val对应final属性,会默认生成getter;而var对应普通可变属性,会默认生成getter和setter(亦可自定义setter和getter,后续会详解)。

对于定义的属性的访问,java需要显式的用getter或setter,而kotlin直接引用名字读取或赋值,如:

java:

rec.getWidth()
rec.setName() //上面没定义,只是个普通可变属性
复制代码

kotlin:

rec.width  //kotlin底层实现也是调用getter
rec.name = "John" //kotlin底层实现也是调用setter
复制代码

对于一些可以即时计算,不需要用字段来支持保存的,可以自定义getter访问器,如判断是否是一个正方形:

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

属性isSquare不需要字段保存它的值。使用如下:

val rec = Rectangle(41,42) //不用写 new
println(rec.isSquare) // 输出false
复制代码

5 枚举

kotlin中的枚举类用了 enum class两个关键字。和java一样,枚举并不是值的列表:可以给枚举类声明属性和方法。

enum class Color(val r:Int,val g:Int,val b:Int){
    RED(255,0,0), GREEN(0,255,0), BLUE(0,0,255);//kotlin中唯一必须用分号的地方
    
    fun rgb() = (r*256+g)*256+b
}
复制代码

**注:**相互之间用逗号分开。当要在枚举类定义任何方法时,就要使用分号把枚举常量列表和方法定义分开,这也是kotlin中唯一必须使用分号的地方

三、 控制结构 -- when

when是switch的替代品,但比swtich更灵活,更强大:

  1. when和if一样,也是个表达式
  2. 不需要写上break;
  3. 不同于java只能在枚举、字符串和数字面值中使用,when可以在结构中使用任意对象;
  4. 多个分支之间,用逗号隔开
//应用上面的枚举类
fun colorString(color: Color) = when (color){
    Color.RED -> "red"
    Color.GREEN -> "green"
    Color.BLUE -> "blue"
    else -> "pure"
}

//应用在任意结构,如set,分支条件是等式检查
fun mix(c1:Color,c2:Color) = when (setOf(c1,c2)){
    setOf(Color.RED,Color.BLUE) -> "red * blue"
    setOf(Color.BLACK,Color.WHITE) -> "black * white"
    else -> "else"
}
复制代码

1 使用不带参数的when

分支条件可以是任意的布尔表达式

上面的例子,每次都需要新建一个set,产生额外的垃圾对象,可以直接使用布尔值表达式来进行对比,如:

fun mixOptimized(c1: Color,c2: Color) = when{
    (c1 == Color.RED && c2 == Color.BLUE) 
            || (c1 == Color.BLUE && c2 == Color.RED) -> "red * blue"
    (c1 == Color.BLACK && c2 == Color.WHITE)
            || (c1 == Color.WHITE && c2 == Color.BLACK) -> "black * white"
    else -> "else"
}
复制代码

2 智能转换is在when中的使用

kotlin中,类型检查和转换是合并的,is检查通过后,就默认转换,如:

class Sum(val left:Int,var right:Int){
    val doubleSum:Int
    get() = 2*field

    init {
        doubleSum = left + right
    }

}

fun printSum(e:Any) = when(e){
    //已经转换
    is Sum -> println("left:${e.left} , right:${e.right}; sum:${e.doubleSum}")
    else -> println("not")
}// 输出:left:2 , right:3; sum:10
复制代码

注:智能转换只在变量经过is检查且之后不会再发生变化的情况下有效。

3 in 在when中的使用

后面有讲解

fun recoginze(c:Char) = when(c){
    in '1'..'9' -> "It's a digit"
    in 'a'..'z' ,in 'A'..'Z' -> "It's a letter"
    else -> "Unknown"
}
复制代码

4 规则--代码块中最后的表达式就是结果

规则--代码块中最后的表达式就是结果 ,在所有使用代码块并期待得到一个结果的地方成立。但不能用于代码块函数体。

如:

fun max(a:Int,b:Int) = if (a>b){
    println()
    a    //返回 a 值
}else b
复制代码

四、 基本迭代

1 while迭代

while迭代,与java一致。

2 for迭代

kotlin for循环只有一种形式,与Java 的 for-each相似:for in

2.1 区间和数列

kotlin没有常规的java for 循环,如,for(int i=0;i<5;i++) 。为替代这种常见的循环,kotlin使用了区间的概念

区间:就是两个值之间的间隔,通常是数字-使用 ..运算符来表示区间(val range = 1..10);常应用于 in!in运算符;

区间有两种常见使用方式:

(1)当作数列迭代,当应用于 int ,long,char时,可当作数列迭代(内部实现了iterator),同时可以控制迭代的步长,如:

    for (i in 1..10){
        println(i)  //输出 1 2 3 4 5 6 7 8 9 10
    } 
    for (i in 10 downTo 1 step 2){
        println(i) //输出 10 9 8 7 6 5 4 3 2 1
    } 
    for (i in 10 downTo 1 step 2) {
        println(i) //输出 10 8 6 4 2
    }
复制代码

(2) 用于“in”检查是否在区间中,此时可用于任意实现了Comparable接口类型,"kotlin" in "java".."scala相当于 "kotlin" <="java" && "kotlin"<="scala"; 同时亦可以用于when中,如:

fun recoginze(c:Char) = when(c){
    in '1'..'9' -> "It's a digit"
    in 'a'..'z' ,in 'A'..'Z' -> "It's a letter"
    else -> "Unknown"
}
复制代码

2.2 迭代map

kotlin有个实用小技巧,即,根据建来访问和更新map,如map[KEY] = VALUE

val map = mapOf(1 to "one",2 to "two") //中缀调用,后续详解
for ((key,value) in map.entries){ //解构声明,后续会详解
      ...
      ...
}
复制代码

2.3 迭代带有下标的list

val list = listOf<String>()
for ((index,element) in list.withIndex()){
    ...
}
复制代码

五、 kotlin中的异常

kotlin中的异常与java基本相似,额外区别:

  1. try,throw都是表达式,而不是语句,可以作为另一个表达式的一部分使用,如:
val number = 9
val percentage = 
    if (number in 0 .. 100)
        number 
    else throw IllegalArgumentException("错误")
复制代码

如果条件满足,percentage正常被number初始化,否则,异常抛出,变量也不会初始化。

  1. 在kotlin中,并没有区分受检异常和非受检异常,所有的异常都可以不显式try catch
关注下面的标签,发现更多相似文章
评论