banner
NEWS LETTER

Kotlin小课堂

Scroll down

写在前面

Andorid第一行代码Kotlin小课堂

变量和函数

变量

Kotlin中定义一个变量,只允许在变量前声明两种关键字:val和var

  • val(value的简写的简写)用来声明一个不可变的变量,这种变量在初始赋值之后就再也不能重新赋值,对应Java中的final变量。
  • var(variable的简写的简写)用来声明一个可变的变量,这种变量在初始赋值之后仍然可以再被重新赋值复制,对应Java中的非final变量。
    Kotlin完全抛弃了Java中的基本数据类型,全部使用了对象数据类型。在Java中int是整型变量的关键字,而在Kotlin中Int变成了一个类,它拥有自己的方法和继承结构。

函数

定义一个函数的语法规则如下:

1
2
3
   fun methodName(param1: Int, param2: Int): Int {
return 0
}
  • fun(function的简写)是定义函数的关键字,无论你定义什么函数,都一定要使用fun来声明。

  • 紧跟在fun后面的是函数名,良好的编程习惯是函数名最好要有一定的意义,能表达这个函数的作用是什么。

  • 函数名后面的一对括号中,可以声明该函数接收什么参数。

  • 括号后面的部分是可选的,用于声明该函数会返回什么类型的数据。如果不需要返回任何数据,这部分可以不写。

  • 两个大括号之间的内容就是函数体了,可以在这里编写函数的具体逻辑。

当一个函数的函数体中只有一行代码时,可以使用单行代码函数的语法糖:

1
fun methodName(param1: Int, param2: Int) = 0
  • 使用这种写法,可以直接将唯一的一行代码写在函数定义的尾部,中间用等号连接即可。

  • return关键字也可以省略,等号足以表达返回值的意思。

  • Kotlin还拥有出色的类型推导机制,可以自动推导出返回值的类型。

逻辑控制

if条件语句

  • Kotlin中的if语句相比于Java有一个额外的功能:它是可以有返回值的,返回值就是if语句每一个条件中最后一行代码的返回值。

    1
    2
    3
    4
    5
    6
    7
    8
    fun largerNumber(num1: Int, num2: Int): Int {
    val value = if (num1 > num2) {
    num1
    } else {
    num2
    }
    return value
    }
  • 仔细观察上述代码,你会发现value其实是一个多余的变量,我们可以直接将if语句返回,这样代码将会变得更加精简,如下所示:

    1
    2
    3
    4
    5
    6
    7
    fun largerNumber(num1: Int, num2: Int): Int {
    return if (num1 > num2) {
    num1
    } else {
    num2
    }
    }
  • 当一个函数只有一行代码时,可以省略函数体部分,直接将这一行代码使用等号串连在函数定义的尾部。虽然largerNumber()函数不止只有一行代码,但是它和只有一行代码的作用是相同的,只是return了一下if语句的返回值而已,符合该语法糖的使用条件。那么我们就可以将代码进一步精简:

    1
    2
    3
    4
    5
    fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) {
    num1
    } else {
    num2
    }
  • 最后,还可以将上述代码再精简一下,直接压缩成一行代码:

    1
    fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) num1 else num2

when条件语句

  • 当需要判断的条件非常多的时候,可以考虑使用when语句来替代if语句。
    1
    2
    3
    4
    5
    6
    7
    fun getScore(name: String) = when (name) {
    "Tom" -> 86
    "Jim" -> 77
    "Jack" -> 95
    "Lily" -> 100
    else -> 0
    }
  • 除了精确匹配之外,when语句还允许进行类型匹配。
    1
    2
    3
    4
    5
    6
    7
    fun checkNumber(num: Number) {
    when (num) {
    is Int -> println("number is Int")
    is Double -> println("number is Double")
    else -> println("number not support")
    }
    }

for-in循环语句

  • 我们可以使用如下Kotlin代码来表示一个区间:

    1
    val range = 0..10
  • 上述代码表示创建了一个0到10的区间,并且两端都是闭区间,这意味着0到10这两个端点都是包含在区间中的,用数学的方式表达出来就是[0, 10]。也可以使用until关键字来创建一个左闭右开的区间:

    1
    val range = 0 until 10
  • 上述代码表示创建了一个0到10的左闭右开区间,它的数学表达方式是[0, 10)。有了区间之后,我们就可以通过for-in循环来遍历这个区间:

    1
    2
    3
    4
    5
    fun main() {
    for (i in 0..10) {
    println(i)
    }
    }
  • 如果你想跳过其中的一些元素,可以使用step关键字:

    1
    2
    3
    4
    5
    fun main() {
    for (i in 0 until 10 step 2) {
    println(i)
    }
    }
  • 如果你想创建一个降序的区间,可以使用downTo关键字:

    1
    2
    3
    4
    5
    fun main() {
    for (i in 10 downTo 1) {
    println(i)
    }
    }

面向对象编程

类与对象

  • 可以使用如下代码定义一个类,以及声明它所拥有的字段和函数:

    1
    2
    3
    4
    5
    6
    7
    8
    class Person {
    var name = ""
    var age = 0

    fun eat() {
    println(name + " is eating. He is " + age + " years old.")
    }
    }
  • 然后使用如下代码创建对象,并对对象进行操作:

    1
    2
    3
    4
    5
    6
    fun main() {
    val p = Person()
    p.name = "Jack"
    p.age = 19
    p.eat()
    }

继承

  • Kotlin中一个类默认是不可以被继承的,如果想要让一个类可以被继承,需要主动声明open关键字:

    1
    2
    3
    open class Person {

    }
  • 要让另一个类去继承Person类,则需要使用冒号关键字:

    1
    2
    3
    4
    class Student : Person() {
    var sno = ""
    var grade = 0
    }
  • 现在Student类中就自动拥有了Person类中的字段和函数,还可以定义自己独有的字段和函数。

接口

  • Kotlin中定义接口的关键字和Java中是相同的,都是使用的interface:
    1
    2
    3
    4
    interface Study {
    fun readBooks()
    fun doHomework()
    }
  • 而Kotlin中实现接口的关键字变量了冒号,和继承使用的是同样的关键字:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Student(val name: String, val age: Int) : Study {
    override fun readBooks() {
    println(name + " is reading.")
    }

    override fun doHomework() {
    println(name + " is doing homework.")
    }
    }

数据类

  • Kotlin中使用data关键字可以定义一个数据类:
    1
    data class Cellphone(val brand: String, val price: Double)
    Kotlin会根据数据类的主构造函数中的参数将equals()、hashCode()、toString()等固定且无实际逻辑意义的方法自动生成,从而大大简少了开发的工作量。

单例类

  • Kotlin中使用object关键字可以定义一个单例类:

    1
    2
    3
    4
    5
    object Singleton {
    fun singletonTest() {
    println("singletonTest is called.")
    }
    }
  • 而调用单例类中的函数比较类似于Java中静态方法的调用方式:

    1
    Singleton.singletonTest()

    这种写法虽然看上去像是静态方法的调用,但其实Kotlin在背后自动帮我们创建了一个Singleton类的实例,并且保证全局只会存在一个Singleton实例。

Lambda编程

集合的创建

  • 使用如下代码可以初始化一个List集合:

    1
    val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
  • 使用如下代码可以初始化一个Set集合:

    1
    val set = setOf("Apple", "Banana", "Orange", "Pear", "Grape")
  • 使用如下代码可以初始化一个Map集合:

    1
    val map = mapOf("Apple" to 1, "Banana" to 2, "Orange" to 3, "Pear" to 4, "Grape" to 5)

Lambda表达式

Lambda就是一小段可以作为参数传递的代码。正常情况下,我们向某个函数传参时只能传入变量,而借助Lambda却允许传入一小段代码。

我们来看一下Lambda表达式的语法结构:

{参数名1: 参数类型, 参数名2: 参数类型 -> 函数体}

首先最外层是一对大括号,如果有参数传入到Lambda表达式中的话,我们还需要声明参数列表,参数列表的结尾使用一个->符号,表示参数列表的结束以及函数体的开始,函数体中可以编写任意行代码,并且最后一行代码会自动作为Lambda表达式的返回值。

集合的函数式API

集合中的map函数是最常用的一种函数式API,它用于将集合中的每个元素都映射成一个另外的值,映射的规则在Lambda表达式中指定,最终生成一个新的集合。比如,这里我们希望让所有的水果名都变成大写模式,就可以这样写:
1
2
3
4
5
6
7
fun main() {
val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
val newList = list.map({ fruit: String -> fruit.toUpperCase() })
for (fruit in newList) {
println(fruit)
}
}
  • 当Lambda参数是函数的最后一个参数时,可以将Lambda表达式移到函数括号的外面。

  • 如果Lambda参数是函数的唯一一个参数的话,还可以将函数的括号省略。

  • 由于Kotlin拥有出色的类型推导机制,Lambda表达式中的参数列表其实在大多数情况下也不必声明参数类型。

  • 当Lambda表达式的参数列表中只有一个参数时,也不必声明参数名,而是可以使用it关键字来代替。

  • 因此,Lambda表达式的写法可以进一步简化成如下方式:

    1
    val newList = list.map { it.toUpperCase() }

Java函数式API

如果我们在Kotlin代码中调用了一个Java方法,并且该方法接收一个Java单抽象方法接口参数,就可以使用函数式API。Java单抽象方法接口指的是接口中只有一个待实现方法,如果接口中有多个待实现方法,则无法使用函数式API。

举个例子,Android中有一个极为常用的点击事件接口OnClickListener,其定义如下:
1
2
3
public interface OnClickListener {
void onClick(View v);
}
可以看到,这是一个单抽象方法接口。假设现在我们拥有一个按钮button的实例,就可以使用函数式API的写法来注册这个按钮的点击事件:
1
2
button.setOnClickListener { v ->
}

空指针检查

  • 空指针是一种不受编程语言检查的运行时异常,只能由程序员主动通过逻辑判断来避免,但即使是最出色的程序员,也不可能将所有潜在的空指针异常全部考虑到。
    1
    2
    3
    4
    public void doStudy(Study study) {
    study.readBooks();
    study.doHomework();
    }
    这段Java代码安全吗?不一定,因为这要取决于调用方传入的参数是什么,如果我们向doStudy()方法传入了一个null参数,那么毫无疑问这里就会发生空指针异常。因此,更加稳妥的做法是在调用参数的方法之前先进行一个判空处理,如下所示:
    1
    2
    3
    4
    5
    6
    public void doStudy(Study study) {
    if (study != null) {
    study.readBooks();
    study.doHomework();
    }
    }

可空类型系统

  • Kotlin中引入了一个可空类型系统的概念,它利用编译时判空检查的机制几乎杜绝了空指针异常。

    1
    2
    3
    4
    fun doStudy(study: Study) {
    study.readBooks()
    study.doHomework()
    }

    这段代码看上去和刚才的Java版本并没有什么区别,但实际上它是没有空指针风险的,因为Kotlin默认所有的参数和变量都不可为空,所以这里传入的Study参数也一定不会为空,可以放心地调用它的任何函数。

    Kotlin提供了另外一套可为空的类型系统,就是在类名的后面加上一个问号。比如,Int表示不可为空的整型,而Int?就表示可为空的整型;String表示不可为空的字符串,而String?就表示可为空的字符串。

    使用可为空的类型系统时,需要在编译时期就把所有的空指针异常都处理掉才行。

判空辅助工具

  • Kotlin提供了一系列的辅助工具,使开发者能够更轻松地进行判空处理。

  • ?. 操作符表示当对象不为空时正常调用相应的方法,当对象为空时则什么都不做。比如:

    1
    2
    3
    if (a != null) {
    a.doSomething()
    }
  • 这段代码使用?.操作符就可以简化成:

    1
    a?.doSomething()
  • ?: 操作符表示如果左边表达式的结果不为空就返回左边表达式的结果,否则就返回右边表达式的结果。比如:

    1
    2
    3
    4
    5
    val c = if (a ! = null) {
    a
    } else {
    b
    }
  • 这段代码的逻辑使用?:操作符就可以简化成:

    1
    val c = a ?: b
  • 结合使用?.操作符和let函数也可以对多次重复调用的某个变量统一进行判空处理:

    1
    2
    3
    4
    5
    6
    fun doStudy(study: Study?) {
    study?.let {
    it.readBooks()
    it.doHomework()
    }
    }

Kotlin中的小魔术

字符串内嵌表达式

在Kotlin中,我们可以直接将表达式写在字符串里面,即使是构建非常复杂的字符串,也会变得轻而易举。
  • Kotlin中字符串内嵌表达式的语法规则如下:

    1
    "hello, ${obj.name}. nice to meet you!"
  • 当表达式中仅有一个变量的时候,还可以将两边的大括号省略:

    1
    "hello, $name. nice to meet you!"

函数的参数默认值

  • Kotlin允许在定义函数的时候给任意参数设定一个默认值,这样当调用此函数时就不会强制要求调用方为此参数传值,在没有传值的情况下会自动使用参数的默认值。语法格式如下:

    1
    2
    3
    fun printParams(num: Int, str: String = "hello") {
    println("num is $num , str is $str")
    }

    这里给printParams()函数的第二个参数设定了一个默认值,这样当调用printParams()函数时,可以选择给第二个参数传值,也可以选择不传,在不传的情况下就会自动使用默认值。

其他文章
目录导航 置顶
  1. 1. 写在前面
  • 变量和函数
    1. 1. 变量
    2. 2. 函数
  • 逻辑控制
    1. 1. if条件语句
    2. 2. when条件语句
    3. 3. for-in循环语句
  • 面向对象编程
    1. 1. 类与对象
  • Lambda编程
    1. 1. 集合的创建
    2. 2. Lambda表达式
    3. 3. 集合的函数式API
    4. 4. Java函数式API
  • 空指针检查
    1. 1. 可空类型系统
    2. 2. 判空辅助工具
  • Kotlin中的小魔术
    1. 1. 字符串内嵌表达式
    2. 2. 函数的参数默认值
  • 请输入关键词进行搜索