Kotlin基础语法学习使用

前言

Kotlin作为一门新的语言,并被Google在17年I/O大会上,成为了官方的Android开发语言。一直也有关注,但也只是简单了解了一下,听说差不多是在java基础上封装了很多语法糖,但当我真正开始学习的时候发现并非如此,当你真正的去学习Kotlin时这种使用及编程时,才会体会到它的强大。


1、函数与变量

(1)先来一个简单的函数声明

fun main(args: Array<String>) {
    println("Hello, world")
}

函数声明以fun关键字开始,没有任何返回的函数。


复杂一些的:函数声明以fun关键字开始 - 函数名 - 函数列表 - 返回类型,里面为函数体。

/**
 * 函数声明以fun关键字开始 - 函数名 - 函数列表 -  返回类型,里面为函数体
 * 返回值的函数,类型Int
 *
 * 语句与表达式的区别在于:
 * 表达式有值,并且能作为另一个表达式的一部分使用
 * 而语句总是包围着它的代码块中的顶层元素,并且没有自己的值
 *
 * 在java中所有的控制语句都是语句,而在Kotlin中除了循环(for、do、do/while)以外大多数控制结构都是表达式
 *
 * 另一方面,java中赋值操作是表达式,而在Kotlin中反而是语句,
 * 这有助于避免比较和赋值之间的混淆,而这种混淆是常见的错误来源
 */
fun max(a: Int, b: Int): Int {
    // 在Kotlin中,if语句是有结果值的表达式,而不是语句,与java中的三元运算符相似:(a > b) ? a : b。
    return if (a > b) a else b
}
代码中已经有非常详细的阐述了,不再做阐述。

更简单的写法:

/**
 * 表达式函数体,与上面的max方法函数一样的结果,更简单
 * 使用单个表达式作为完整的函数体,并去掉花括号和return语句
 *
 * 如果函数体写在花括号中,则称这个函数有代码块体,如果直接返回一个表达式,它就有表达式体
 */
fun max2(a: Int, b: Int): Int = if (a > b) a else b
进一步简化max函数,省掉返回类型:

/**
 * 进一步简化max函数,省掉返回类型,与上面的max方法函数一样的结果
 *
 * 每个变量、表达式和函数都有返回类型,编译器会自行类型推导
 *
 * 注意:只有表达式体函数的返回类型可以省略。
 * 而对于有返回值的代码块体函数,必须显示地写出返回类型和return语句
 */
fun max3(a: Int, b: Int) = if (a > b) a else b

(2)变量声明

/**
 * 省略了类型声明,也可以显式的指定变量的类型
 *
 * 有一个变量初始化器
 *
 * 如果不指定变量的类型,编译器会分析初始化器表达式的值,并把它的类型作为变量的类型
 */
val question = "aa"
val answer = 12
val answer_2: Int = 12
// 7.5 * 10^6
val yearsToCompute = 7.5e6

如果变量没有初始化器,需要显式地指定它的类型,如果不能提供可以赋值给这个变量的值的信息,编译器就无法推断它的类型:

  /**
     * 如果变量没有初始化器,需要显式地指定它的类型
     *
     * 如果不能提供可以赋值给这个变量的值的信息,编译器就无法推断它的类型
     */
    val answer3: Int
    answer3 = 12

打印非常简单的使用:

    // 打印,几乎里面能够直接传入非常多的数据类型
    println(answer3)


变量声明关键字有:val和var

val ------- > 来自value,不可变引用,对应java中的final变量。

var ------- >来自variable,可变引用,对应java普通非final变量。


更简单的字符串格式化:字符串模板

 val name = "lh"
 println("name is: $name")

2、类和属性

(1)类、属性的声明:冒号后面接的就是接口

package com.lh.customviewlearn.testkotlin

import java.io.Serializable

/**
 * 只有数据没有其他代码通常称为值对象
 *
 * 类默认public
 * val属性只读属性:默认会自动生成一个存储值的字段和一个简单的getter访问器方法
 * var属性可变(可写)属性:生成一个字段和一个getter、一个setter访问器方法
 *
 * 也可声明自定义的访问器,使用不同的逻辑来计算和更新属性的值
 *
 * @author LuoHao
 * Created on 2018/4/5 11:52
 */
class Person(
        val name: String,
        var isMarried: Boolean
) : Serializable

(2) 使用类

java 中:可以看到和java类使用没什么区别

        Person p = new Person("", true);
        p.getName();
        p.isMarried();
        p.setMarried(false);

Kotlin 中:调用构造方法不需要关键字new,可以直接访问对象属性,但调用的是getter。

    /**
     * 类的使用
     */
    // 调用构造方法不需要关键字new
    val person = Person("luohao", true)
    // Kotlin中,可以直接访问对象属性,但调用的是getter
    println(person.name)
    println(person.isMarried)
    // 直接赋值
    person.isMarried = false

(3)自定义访问器

如下声明属性的getter

/**
 * 自定义访问器
 *
 * @author LuoHao
 * Created on 2018/4/6 21:38
 */
class Rectangle(val height: Int, val width: Int) {
    // 声明属性的getter
    // 自定义访问器,通过计算是否相等判断是否是正方形,不需要一个单独的字段来存储这个信息(是否是正方形)
    // 属性isSquare不需要字段来保存它的值,它只是一个自定义实现的getter,它的值是每次访问属性时计算出来的
    val isSquare: Boolean
        get() {
            return height == width
        }
    // 注意:如果在java中访问这个属性,可以调用 isSquare()方法
    // 同等实现,不需要带花括号的完整语法,对这个属性的调用依然不变
    val isSquare2: Boolean
        get() = height == width
	
    val square: Boolean = height == width
    // val square: Boolean = if (height == width) true else false

    fun isSquare4(): Boolean = true
}
/**
 * 顶层函数 可以直接写在类的外面,这点和java不同
 * Kotlin 中会导入顶层函数的函数名称来使用该函数
 */
fun isSquare4(): Boolean = false

 java中使用: 
 

        Rectangle rectangle = new Rectangle(1, 3);
        rectangle.isSquare(); // 对应 val isSquare: Boolean属性
        rectangle.isSquare2(); // 对应 val isSquare2: Boolean属性
        rectangle.isSquare4(); // 对应Rectangle类里面声明的fun isSquare4():Boolean 方法
        isSquare4(); // 对应 顶层函数 fun isSquare4():Boolean 方法

 Kotlin 中使用: 
 

val rectangle = Rectangle(1, 2)
    println(rectangle.isSquare)
    println(rectangle.square)
    println(rectangle.isSquare2)

    rectangle.isSquare4()
    // Kotlin 中会导入顶层函数的函数名称来使用该函数
    isSquare4()

(4)Kotlin 中源码布局:目录和包结构,这点和java不同

在Rectangle.kt这个文件中,你可以做的事情有很多,如下:

/**
 * 包层级结构不需要遵循目录层级结构,跟java刚好相反,当然推荐遵循java的目录布局结构是个不错的实践
 * 但是也应该毫不犹豫地把多个类放进同一个文件中,特别是那些很小的类(在Kotlin中,类通常很小)
 *
 * kotlin 中可以将多个类放在同一个文件中,并且文件的名字还可以随意选择
 * 意思就是:多个类的类名都可以完全和文件的名称不一样,这点跟java完全不一样,java必须类名和文件名相同
 * 如将上面这个Rectangle类名字改为其他名字,文件名依然叫Rectangle.kt,也不会有错
 */
class Person_2(
        val name: String,
        var isMarried: Boolean
)

/**
 * 声明变量
 */
val isSquare: Boolean = true

/**
 * 顶层函数
 * Kotlin 中会导入顶层函数的函数名称来使用该函数
 */
fun isSquare4(): Boolean = false

fun createRandomRectangle(): Rectangle {
    val random = Random()
    return Rectangle(random.nextInt(), random.nextInt())
}

如上,基本都是这些了,是不是和java不一样,哈哈~

3、Kotlin 中的控制结构:表示和处理选择:枚举和when,when 结构 可以替代java中 switch 结构,但 when 结构更强大。

(1)先来看下Kotlin 中声明枚举,Kotlin 中定义枚举,比java中多了 class 关键字,Kotlin 中 enum 是一个所谓的软关键字:只有当它出现在 class 前面时才有特殊意义,在其他地方可以把它当做,普通的名称使用。与此不同的是,class 仍然是个关键字。

/**
 * Kotlin 中定义枚举,比java中多了 class 关键字
 * Kotlin 中 enum 是一个所谓的软关键字:只有当它出现在 class 前面时才有特殊意义,在其他地方可以把它当做
 * 普通的名称使用。与此不同的是,class 仍然是个关键字
 */
enum class Color {
    RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}
可以给枚举类声明属性和方法:在每个常量创建的时候必须指定属性值,并且最后一个后面必须像java一样需要有分号,这个分号是 Kotlin 语法中唯一必须使用分号的地方:如果要在枚举中定义任何的方法,就要使用分号把枚举常量列表和方法定义分开。

使用 when 来处理枚举类。

/**
 * 和 java 一样,枚举并不是值的列表:可以给枚举类声明属性和方法。
 * 如下声明了枚举常量的属性
 */
enum class Color2(val r: Int, val g: Int, val b: Int) {
    /**
     * 在每个常量创建的时候必须指定属性值,并且最后一个后面必须像java一样需要有分号
     *
     * 这个分号是 Kotlin 语法中唯一必须使用分号的地方:如果要在枚举中定义任何的方法,
     * 就要使用分号把枚举常量列表和方法定义分开。
     */
    RED(255, 0, 0),
    YELLOW(255, 156, 120),
    WHITE(255, 120, 1),
    ORANGE(255, 165, 0);

    /**
     * 给枚举类定义一个方法
     */
    fun rgb(): Int {
        return (r * 256 + g) * 256 + b
    }

    fun rgb2() = (r * 256 + g) * 256 + b

    // 使用 when 来处理枚举类
    fun getMnemonic3(color: Color2): String {
        return when (color) {
        // 枚举常量的完整名称使用
            Color2.RED -> "RED"
        // 枚举常量直接名称使用
            ORANGE -> "ORANGE"
            Color2.YELLOW -> "YELLOW"
            Color2.WHITE -> "WHITE"
        }
    }

    // 使用 when 来处理枚举类
    fun getMnemonic2(color: Color2): String {
        val s = when (color) {
        // 枚举常量的完整名称使用
            Color2.RED -> {
                "RED"
            }
        // 枚举常量直接名称使用
            ORANGE -> "ORANGE"
            Color2.YELLOW -> "YELLOW"
            Color2.WHITE -> "WHITE"
        }
        return s
    }

    // 使用 when 来处理枚举类
    fun getMnemonic(color: Color2): String {
        val s: String
        s = when (color) {
        // 枚举常量的完整名称使用
            Color2.RED -> "RED"
        // 枚举常量直接名称使用
            ORANGE -> "ORANGE"
            Color2.YELLOW -> "YELLOW"
            Color2.WHITE -> "WHITE"
        }
        return s
    }
}

写在类外面:

/**
 * 使用 when 来处理枚举类 直接返回一个 when 表达式,返回对应的字符串
 * 不需要每个分支都写上 break 语句,如果匹配成功只会对应的分支执行
 * 也可以把多个配置值合并到同一个分支,使用逗号分隔即可
 */
fun getMnemonic(color: Color2) =
        when (color) {
        // 把多个配置值合并到同一个分支,使用逗号分隔即可
            Color2.RED, Color2.YELLOW -> "RED"
            Color2.ORANGE -> "ORANGE"
        // 其他类型使用 else
            else -> "Other Value"
        }
使用:

fun testColor() {
    Color2.ORANGE.getMnemonic2(Color2.RED)
    println(getMnemonic(Color2.ORANGE))
}
when 结构中可以使用任意对象,java的switch中必须使用常量(枚举常量、字符串、数字值)作为分支条件:组合条件

// when 结构中可以使用任意对象,java的switch中必须使用常量(枚举常量、字符串、数字值)作为分支条件
// 组合条件
fun mix(c1: Color2, c2: Color2) =
// setOf 是创建一个Set对象
        when (setOf(c1, c2)) {
            setOf(Color2.RED, Color2.ORANGE) -> Color2.ORANGE
            setOf(Color2.YELLOW, Color2.WHITE) -> Color2.WHITE
        // 匹配不到,其他情况
            else -> throw Exception("Dirty color")
        }
mix方法的优化:

/**
 * 不带任何参数的 when
 *
 * 该方法是上面mix方法的优化,原因是:上面的方法会频繁创建Set对象,
 * 使用这个方法可以避免创建额外的垃圾对象
 *
 * 如果没有给 when 表达式提供参数,分支条件就是任意的布尔表达式。
 */
fun mixOptimized(c1: Color2, c2: Color2) =
        when {
            (c1 == Color2.RED && c2 == Color2.ORANGE) ||
                    (c1 == Color2.ORANGE && c2 == Color2.RED) ->
                Color2.ORANGE

            (c1 == Color2.WHITE && c2 == Color2.YELLOW) ||
                    (c1 == Color2.YELLOW && c2 == Color2.WHITE) ->
                Color2.WHITE
            else -> throw Exception("Dirty color")
        }

(2)Kotlin 中 智能转换:合并类型检查和转换。来进行(1+2)+4 简单的运算表达式求值。

声明一个接口和两个类:

// 智能转换:合并类型检查和转换,(1+2)+4 简单的运算表达式求值,存储在一个树状结构中Num永远是叶子节点,
interface Expr

// 接口实现使用冒号(:)后加接口名称,简单的值对象,只有一个属性value,实现了Expr接口
class Num(val value: Int) : Expr

// Sum 运算的实参可以是任何的Expr:Num或者另一个Sum,Sum类存储了Expr类型的实参left和right的引用
class Sum(val left: Expr, var right: Expr) : Expr {
    val right2: Expr
        get() {
            return right
        }

    // 注意 var 和val 自定义访问器的不同赋值方式
    var left2: Expr = Num(1)
        get() {
            return left
        }
}
需要计算的为:Sum(Sum(Num(1) + Num(2)), Num(4))

// Sum(Sum(Num(1) + Num(2)), Num(4))
// Expr接口有两种实现:1,如果表达式是一个数字,直接返回它的值;
// 2、如果是一次求和,得先计算左右两个表达式的值,再返回它们的和
fun eval(e: Expr): Int {
    // Kotlin 中方法中的变量是val类型的,不能重新复制
    // e = Num(1)

    // Kotlin 中使用 is 关键字来检查判断一个变量是否是某种类型,与java中的 instanceOf 相似
    // 但在java还需要显示的加上类型转换;
    // 而Kotlin 中则不需要,直接当做检查过的类型使用,是编译器执行了类型转换,这种行为称为智能转换。
    if (e is Num) {
        // 显示地转换成类型Num是多余的
        val n = e as Num
        return n.value
    }
    if (e is Sum) {
        // 变量 e 被智能的转换了类型
        return eval(e.right) + eval(e.left) + eval(e.left2) + eval(e.right2)
    }
    throw IllegalArgumentException("Unknown expression")
}
优化,使用有返回值的if作为函数体:

// java 中三元运算符 if(a > b) a else b 和 a > b ? a : b
// Kotlin 没有,因为if表达式有返回值,可以使用表达式体语法重写eval函数,
// 去掉return语句和花括号,使用有返回值的if表达式作为函数体
fun evalOptimized(e: Expr): Int =
        if (e is Num)
        // 如果if分支中只有一个表达式,花括号是可以省略的,
            e.value + 1
        else if (e is Sum) {
            // 如果if分支是一个代码块,需要花括号,代码块中的最后一个表达式会被作为结果返回的
            if (e.left is Num) e.left.value + 1
            evalOptimized(e.right) + evalOptimized(e.left)
        } else {
            throw IllegalArgumentException("UnKnown expression")
        }
再次优化 使用 when 代替if层叠:

/**
 * 再次优化 使用 when 代替if层叠
 *
 * when 表达式并不仅限于检查值是否相等,这里是另一种when分支的形式,允许检查when实参值的类型,并应用了智能转换
 *
 * 当分支逻辑太过复杂时,可以使用代码块作为分支体,并且此时必须使用花括号,代码块中的最后一个表达式会被作为结果返回的
 */
fun evalOptimized2(e: Expr): Int =
        when (e) {
        // 应用了智能转换
        // 检查实参类型的 when 分支
            is Num -> {
                // 如果if分支是一个代码块,需要花括号,代码块中的最后一个表达式会被作为结果返回的
                println("num: ${e.value}")
                e.value
            }
            is Sum -> {
                // evalOptimized2(e.left) + evalOptimized2(e.right)
                val left = evalOptimized2(e.left)
                val right = evalOptimized2(e.right)
                println("sum: $left + $right")
                // 规则:代码块中最后的表达式就是结果,在所有代码块并期望得到一个结果的地方成立。
                // 但该规则对于常规函数不成立。
                left + right
            }
            else ->
                throw IllegalArgumentException("Unknown expression")
        }
使用:

fun textNum() {
    println(eval(Sum(Sum(Num(1), Num(2)), Num(4))))
    println(evalOptimized(Sum(Sum(Num(1), Num(2)), Num(4))))
    println(evalOptimized2(Sum(Sum(Num(1), Num(2)), Num(4)))) 
}


4、迭代事物:while 循环 和 for 循环

(1)while 循环和java中没什么区别

// 迭代事物:while 循环 和 for 循环
// while 循环和java中没什么区别
fun whileTest() {
    val condition = false
    // 当condition为true时才执行循环体
    while (condition) {

    }
    // 循环体第一次会无条件地执行。此后,当condition为true时才执行
    do {

    } while (condition)
}
(2)迭代数字:区间和数列

Kotlin 中没有常规的java for循环,Kotlin使用了区间概念,如下:

// 迭代数字:区间和数列
// Kotlin 中没有常规的java for循环,Kotlin使用了区间概念
// 区间本质上就是两个值之间的间隙,这两个值通常是数字:一个起始值,一个结束值。使用(..)运算符来表示区间:
// 如 val oneToTen = 1..10 注意:Kotlin 中区间是闭合的或者包含的,意味着第二个值(10)也是区间的一部分
// 循环迭代其中所有的值,如果能迭代区间所有的值,这样的区间被称作数列。
fun fizzBuzz(i: Int) = when {
// % 和java一样是取模运算符,是否能被15整除
    i % 15 == 0 -> "FizzBuzz "
    i % 5 == 0 -> "Fizz "
    i % 3 == 0 -> "Buzz "
    else -> "$i "
}
使用:

// 区间
    for (i in 1..100) {
        print(fizzBuzz(i))
    }
迭代带步长的区间,迭代一个带步长的数列,允许跳过一些数字,步长也可以是负数,负数时是递减的。

从100开始倒着计数并且只记偶数:

    // 迭代带步长的区间,迭代一个带步长的数列,允许跳过一些数字,步长也可以是负数,负数时是递减的
    // 从100开始倒着计数并且只记偶数
    // 100 downTo 1 表示是递减的数列(步长为-1),然后step把步长的绝对值变为了2,但方向保持不变(事实上,步长被设置成了-2)
    // ..语法始终创建的是包含结束值(100)的区间。若不想包含则是半闭合区间,使用 until 函数可以创建。
    for (i in 100 downTo 1 step 2) {
        print(fizzBuzz(i))
    }

语法始终创建的是包含结束值(100)的区间。若不想包含则是半闭合区间,使用 until 函数可以创建。

    // ..语法始终创建的是包含结束值(100)的区间。若不想包含则是半闭合区间,使用 until 函数可以创建。
    // for (i in 0 until 100)等同于 for (i in 0..100 - 1)
    // 但推荐这个,更清晰地表达了意图
    for (i in 0 until 100) {
        print(fizzBuzz(i))
    }
    // 不推荐
    for (i in 0..100 - 1) {
        print(fizzBuzz(i))
    }
(3)迭代map,迭代集合

    // 使用TreeMap 让键排序
    val binaryReps = TreeMap<Char, String>()
    // .. 语法不仅可以创建数字区间,还可以创建字符区间
    // 使用字符区间迭代A到F的字符,包括F
    for (c in 'A'..'F') {
        // ASCII码转换成二进制
        val binary = Integer.toBinaryString(c.toInt())
        // binaryReps.put(c, binary)
        // 推荐这个赋值,不需要使用put 和 get
        binaryReps[c] = binary
        // 获取值
        // val aa = binaryReps[c]
    }

    // 迭代map,把键和值赋给两个变量
    for ((letter, binary) in binaryReps) {
        println("$letter = $binary")
    }
展开语法在迭代集合的同时跟踪当前项的下标,不需要创建一个单独的变量来存储下标并手动增加它:

    // 展开语法在迭代集合的同时跟踪当前项的下标,不需要创建一个单独的变量来存储下标并手动增加它
    val list = arrayListOf("10", "11", "12")
    // 迭代集合时使用下标
    for ((index, element) in list.withIndex()) {
        println("$index: $element")
    }
    // 打印: 0: 10   1: 11  2: 12
关键字in不仅可以迭代区间和集合,还可以用in来检查区间或集合是否包含了某个值,或者使用in的逆运算 !in
// 关键字in不仅可以迭代区间和集合,还可以用in来检查区间或集合是否包含了某个值
// 使用in的逆运算 !in

// 判断字符是否是英文字母
fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'

// String类型匹配
fun isLetter2(s: String) = s in "a".."z" || s in "A".."Z"

// 判断不是数字
fun isNotDigit(c: Char) = c !in '0'..'9'

// 用in检查作为when表达式
fun recognize(c: Char) = when (c) {
    in '0'..'9' -> "Tt's a digit!"
// 逗号隔离多种检查的区间
    in 'a'..'z', in 'A'..'Z' -> "It's a letter!"
    else -> "I don't know..."
}
使用:

    // 关键字in不仅可以迭代区间和集合,还可以用in来检查区间或集合是否包含了某个值
    // 使用in的逆运算 !in
    println(isLetter('a')) //  true
    println(isLetter2("a")) // true
    println(isNotDigit('b')) // true

    println(recognize('8'))
 in 支持实例比较操作的任意类(实现了 Comparable 接口的类),就能创建这种类型的对象的区间:

// in 支持实例比较操作的任意类(实现了 Comparable 接口的类),就能创建这种类型的对象的区间
    println("Kotlin" in "Java".."Scala") // true
in 检查同样适用于集合 如 Set:

    // in 检查同样适用于集合 如 Set
    println("Kotlin" in setOf("Java", "Scala")) // false
5、Kotlin 中的异常

// Kotlin 中的异常
// 异常处理和java中相似,函数可以正常结束,也可以在出现错误的时候抛出异常
// 方法的调用者能捕获这个异常并能处理它,如果没有被处理,异常将沿着调用栈再次抛出
fun dealException(percentage: Int) {
    if (percentage !in 0..100) {
        throw IllegalArgumentException("A percentage value must be between 0 and 100: $percentage")
    }

    // throw 在Kotlin中,相比java中不同的是,Kotlin 中throw结构是一个表达式,能作为另一个表达式的一部分使用
    // 注意:在Kotlin 中可以创建和方法参数一样的变量
    val number =
            if (percentage in 0..100) percentage
            else throw IllegalArgumentException("A percentage value must be between 0 and 100: $percentage")
}
try、catch和finally:

Kotlin中方法声明中不必显示的指定函数throws可能抛出的异常,Kotlin 中并不区分受检查异常和未受检查异常,不用指定函数抛出的异常,可以处理也可以不处理。

// try、catch和finally
/**
 * 读取文件中一行的内容,解析数字,抛异常
 * Kotlin中方法声明中不必显示的指定函数throws可能抛出的异常,
 * 需要知道Kotlin 中并不区分受检查异常和未受检查异常,不用指定函数抛出的异常,可以处理也可以不处理
 */
fun readNumber(reader: BufferedReader): Int? {
    // 读取文件中一行的内容,解析数字
    try {
        val line = reader.readLine()
        return Integer.parseInt(line)
    } catch (e: NumberFormatException) {
        e.printStackTrace()
        // return null
    } finally {
        // finally的作用和java 一样
        reader.close()
    }
    return null
}
try 作为表达式,可以把它的值赋值给一个变量  将final去掉:

注意:try catch finally 中总是需要用花括号把语句主体(就一句也需要)括起来,如果有多个表达式,那么最后一个表达式就是返回值

/**
 * try 作为表达式,可以把它的值赋值给一个变量  将final去掉
 *
 * 并且try catch finally 中总是需要用花括号把语句主体(就一句也需要)括起来,
 * 如果有多个表达式,那么最后一个表达式就是返回值
 */
fun readNumber2(reader: BufferedReader) {
    // 读取文件中一行的内容,解析数字
    val number = try {
        // 第一条表达式
        val test = 1
        // 变为try表达式的值
        Integer.parseInt(reader.readLine()) + test
    } catch (e: NumberFormatException) {
        return
    }
    println(number)
}
测试:

    val reader = BufferedReader(StringReader("not a number"))
    // 不会有任何输出,因为catch中return了
    readNumber2(reader)
readNumber2 方法中将return语句放在了catch代码块中,因此该函数的执行在catch代码块之后就不会继续了。

如果想要继续执行后面的代码,catch子句也需要有一个值,它将是子句中最后一个表达式的值。

/**
 * readNumber2 方法中将return语句放在了catch代码块中,因此该函数的执行在catch代码块之后就不会继续了。
 * 如果想要继续执行后面的代码,catch子句也需要有一个值,它将是子句中最后一个表达式的值
 */
fun readNumber3(reader: BufferedReader) {
    // 读取文件中一行的内容,解析数字
    val number = try {
        // 第一条表达式
        val test = 1
        // 变为try表达式的值
        Integer.parseInt(reader.readLine()) + test
    } catch (e: NumberFormatException) {
        // 发生异常的时候直接赋值为null
        null
    }

    // 发生异常的时候打印值为null
    println(number)
}

到此,Kotlin 的基础差不多了,可以使用Kotlin进行开发了。

总结一下:

1、fun 关键字 用来声明函数。

2、val  关键字 用来声明只读变量。

3、var 关键字 用来声明可变变量。

4、字符串模板帮助你避免繁琐的字符串连接。在变量名称前加上 $ (美元符号)前缀或者用 ${ } 包围一个表达式,来把值注入到字符串中。

5、值对象类在Kotlin 中以简洁的方式表示。

6、熟悉的 if 现在是带返回值的表达式。

7、when 表达式类似于java中的switch但功能更强大。

8、在检查过变量具有某种类型之后不必显式地转换它的类型:编译器使用智能转换自动帮你完成。

9、for、while和do-while 循环与java类似,但for循环现在更加方便,特别是当你需要迭代map的时候,又或是迭代集合需要下标的时候。

10、简洁的语法1..10会自动创建一个区间。区间和数列允许Kotlin在for循环中使用统一的语法和同一套抽象机制,并且还可以使用 in 运算符和 !in 运算符来检查值是否属于某个区间。

11、Kotlin 中的异常处理和java 非常相似,除了Kotlin 不要求你声明函数可以抛出的异常。


说明一下:本文是按照《Kotlin in Action》这本书来进行学习的。


猜你喜欢

转载自blog.csdn.net/u012430727/article/details/79859413