Kotlin系列一:基础知识快速入门

2022-01-08 14:50:47 浏览数 (1)

一 概述

Android 1.5系统中Google 引入了NDK功能,支持使用C和C 语言来进行一些本地化开发。

17年官宣kotlin为安卓一级开发语言;所以对于安卓开发者,学习kotlin是必须的,这是java最好的第三方库(富语法糖java)。

kotlin将代码编译成同样规格的class文件让Java虚拟机识别,它继承了Java的全部财富,和Java100%兼容的,可以直接调用使用Java编写的代码,也可以无缝使用Java第三方的开源库;Kotlin的类型推断也在Java 8 中被推出,Lambda作为函数式编程的基础也在Java 8版本中加入,两者会越来越像。

Kotlin是函数式编程语言(注1:),Kotlin的语法非常像Scala,python,借鉴了很多语言,学了很多语言发现他们越来越像,就像C#之父Anders Hejlsberg说的:未来的编程语言将逐渐融合各自的特性,而不会只存在单纯的声明式语言或者函数编程语言。

对比Java的一些优势:

与作为命令式语言时代的产物:Java相比,Kotlin对内联函数的支持,使它运行Lambda表达式更快;

提前到编译期的空指针安全检测;

Kotlin有很多现代静态编程语言的特点:类型推断、多范式支持、可空性表达、扩展函数、DSL支持等。这些功能Java也陆续在加入。(2021.5.5注:该句有歧义,Koltlin本身就是静态语言,不要误以为其为动态语言)

二 基本类型

Kotlin完全抛弃了Java中的基本数据类型,全部使用了对象数据类型,在 Kotlin 中,所有东西都是对象;

2.1 数字

基本Kotlin数值类型包括:Byte、Short、Int、Long、Float、Double等。与Java不同的是,Kotlin中的Charactor不属于数值类型。

Kotlin 提供了一组表示数字的内置类型。 对于整数,有四种不同大小的类型,因此值的范围也不同。

类型

大小(比特数)

最小值

最大值

Byte

8

-128

127

Short

16

-32768

32767

Int

32

-2,147,483,648 (-231)

2,147,483,647 (231 - 1)

Long

64

-9,223,372,036,854,775,808 (-263)

9,223,372,036,854,775,807 (263 - 1)

所有以未超出 Int 最大值的整型值初始化的变量都会推断为 Int 类型。如果初始值超过了其最大值,那么推断为 Long 类型。 如需显式指定 Long 型值,请在该值后追加 L 后缀。

代码语言:txt复制
val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // Long
val oneByte: Byte = 1

对于浮点数,Kotlin 提供了 Float 与 Double 类型。

类型

大小(比特数)

有效数字比特数

指数比特数

十进制位数

Float

32

24

8

6-7

Double

64

53

11

15-16

对于以小数初始化的变量,编译器会推断为 Double 类型。 如需将一个值显式指定为 Float 类型,请添加 f 或 F 后缀。 如果这样的值包含多于 6~7 位十进制数,那么会将其舍入。

代码语言:txt复制
val pi = 3.14 // Double
val e = 2.7182818284 // Double
val eFloat = 2.7182818284f // Float,实际值为 2.7182817

请注意,与一些其他语言不同,Kotlin 中的数字没有隐式拓宽转换。 例如,具有 Double 参数的函数只能对 Double 值调用,而不能对 Float、 Int 或者其他数字值调用。

注:=== 表示比较对象地址,== 表示比较两个值大小。

2.2 字符类型

Kotlin中字符类型用Charactor表示,与Java不同的是,它们不能直接当作数字,它无法直接使用Char类型的ASCII进行算数运算。如果需要用,得用toInt函数转为相应ASCII。

2.3 布尔型

一些语言比如OC或python中,false、true可以用0、1代替,在Kotlin/Java中不允许。

2.4 数组类型

Kotlin中数组用Array类表示,可以使用库函数 arrayOf() 来创建一个数组并传递元素值给它。Kotlin 也有无装箱开销的专门的类来表示原生类型数组: ByteArray、 ShortArray、IntArray 等等。这些类与 Array 并没有继承关系,但是它们有同样的方法属性集。

代码语言:txt复制
// 大小为 5、值为 [0, 0, 0, 0, 0] 的整型数组
val arr = IntArray(5)

// 例如:用常量初始化数组中的值
// 大小为 5、值为 [42, 42, 42, 42, 42] 的整型数组
val arr = IntArray(5) { 42 }

// 例如:使用 lambda 表达式初始化数组中的值
// 大小为 5、值为 [0, 1, 2, 3, 4] 的整型数组(值初始化为其索引值)
var arr = IntArray(5) { it * 1 }

2.5 字符串

Kotlin中字符串同Java,用String表示。

字符串用 String 类型表示。字符串是不可变的。 字符串的元素——字符可以使用索引运算符访问: si。 可以用 for 循环迭代字符串。

Kotlin提供了字符串的内嵌表达式,也被称为字符串模板。语法规则:

代码语言:txt复制
"hello, ${obj.name}. nice to meet you!"

字符串里嵌入${}这种语法结构的表达式,并在运行时使用表达式执行的结果替代这一部分内容,当表达式中仅有一个变量的时候,还可以将两边的大括号省略:

代码语言:txt复制
"hello, $name. nice to meet you!"

三 类型转换和变量定义

3.1 类型转换

精度缺失的问题就不多说了,一般两个不同类型数字在逻辑运算时并不需要特别注意,算术运算符会重载适应不同类型:

代码语言:txt复制
val a = 1L   3  // Long   Int => Long

显示转换:

类型

显示转换

Byte

toByte()

Short

toShort()

Int

toInt()

Long

toLong()

Float

toFloat()

Double

toDouble()

Char

toChar()

String

toString()

3.2 变量

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

  1. val(value的简写)用来声明一个不可变的变量,这种变量在初始赋值之后就再也不能重新赋值,对应Java中的final变量。
  2. var(variable的简写)用来声明一个可变的变量,这种变量在初始赋值之后仍然可以再被重新赋值,对应Java中的非final变量。

这样设计,是为了解决Java中final关键字没有被合理使用的问题。

使用指导:永远优先使用val来声明一个变量,而当val没有办法满足你的需求时再使用var。

Kotlin的类型推导机制:如下,val关键字定义了一个变量a赋值为10,这里a就会被自动推导成整型变量。

代码语言:txt复制
fun main() {
    val a = 10
    println("a = "   a)
}

但对一个变量延迟赋值的话,Kotlin就无法自动推导它的类型了。这时候就需要显式地声明变量类型才行:

代码语言:txt复制
val a: Int = 10

四 函数

Kotlin函数语法规则:

代码语言:txt复制
fun methodName(param1: Int, param2: Int): Int {
    return 0
}

参数括号后面的部分用于声明该函数会返回什么类型的数据,上例表示该函数会返回一个Int类型的数据。

如果一个函数不返回任何有用的值,它的返回类型是 Unit。如果函数不需要返回任何数据,这部分可以不写。

当一个函数中只有一行代码时,Kotlin允许我们不必编写函数体,可以直接将唯一的一行代码写在函数定义的尾部,中间用等号连接即可:

代码语言:txt复制
fun largerNumber(num1: Int, num2: Int): Int = max(num1, num2)

再结合Kotlin出色的类型推导机制,由于max()函数返回的是一个Int值,因此Kotlin可以推导出largerNumber()函数返回的必然也是一个Int值,代码可以进一步简化成如下形式:

代码语言:txt复制
fun largerNumber(num1: Int, num2: Int) = max(num1, num2)

默认参数:

函数参数可以有默认值,当省略相应的参数时使用默认值。与其他语言相比,这可以减少重载数量:

代码语言:txt复制
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) { /*……*/ }

如果在默认参数之后的最后一个参数是 lambda表达式,那么它既可以作为具名参数在括号内传入,也可以在括号外传入:

代码语言:txt复制
fun foo(bar: Int = 0, baz: Int = 1, qux: () -> Unit) { /*……*/ }

foo(1) { println("hello") }     // 使用默认值 baz = 1
foo(qux = { println("hello") }) // 使用两个默认值 bar = 0 与 baz = 1
foo { println("hello") }        // 使用两个默认值 bar = 0 与 baz = 1

Kotlin函数的可见性修饰符和Java相比变化较大。

Java和Kotlin中函数可见性修饰符之间的区别:

修饰符

Java

Kotlin

public

所有类可见

(默认类型)所有类可见

private

当前类可见

当前类可见

protected

当前类、子类、同一包路径下的类可见

当前类、子类可见

default

(默认类型)同一包路径下的类可见

internal

同一模块中的类可见

五 控制流:if、when、for、while

5.1 if条件语句

代码语言:txt复制
fun largerNumber(num1: Int, num2: Int): Int {
    var value = 0
    if (num1 > num2) {
        value = num1
    } else {
        value = num2
    }
    return value
}

Kotlin中的if语句相比于Java有一个额外的功能,它是可以有返回值的,返回值就是if语句每一个条件中最后一行代码的返回值。因此,上述代码就可以简化成如下形式:

代码语言:txt复制
fun largerNumber(num1: Int, num2: Int): Int {
    return if (num1 > num2) {
        num1
    } else {
        num2
    }
}

kotlin的语法糖里,当一个函数只有一行代码时,可以省略函数体部分,直接将这一行代码使用等号串连在函数定义的尾部。虽然上述代码中的largerNumber()函数不止只有一行代码,但是它和只有一行代码的作用是相同的,只是返回了一下if语句的返回值而已,符合该语法糖的使用条件。那么我们就可以将代码进一步精简:

代码语言:txt复制
fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) {
    num1
} else {
    num2
}

再精练:

代码语言:txt复制
fun largerNumber(num1: Int, num2: Int) = if (num1 > num2) num1 else num2

5.2 when条件语句

Kotlin中的when语句类似于Java中的switch语句,但比Java中的switch语句要灵活很多。Java中的switch只能传入整型或短于整型的变量作为条件,JDK 1.7之后增加了对字符串变量的支持。

编写一个查询考试成绩的功能,输入一个学生的姓名,返回该学生考试的分数,if写法:

代码语言:txt复制
fun getScore(name: String) = if (name == "Tom") {
    86
} else if (name == "Jim") {
    77
} else if (name == "Jack") {
    95
} else if (name == "Lily") {
    100
} else {
    0
}

when语句允许传入一个任意类型的参数,然后可以在when的结构体中定义一系列的条件,格式是:

代码语言:txt复制
匹配值 -> { 执行逻辑 }

当你的执行逻辑只有一行代码时,{ }可以省略。

当使用when语句的时候,现在我们将代码改成如下写法:

代码语言:txt复制
fun getScore(name: String) = when (name) {
    "Tom" -> 86
    "Jim" -> 77
    "Jack" -> 95
    "Lily" -> 100
    else -> 0
}

除了精确匹配之外,when语句还允许进行类型匹配。举个例子。定义一个checkNumber()函数,如下所示:

代码语言:txt复制
fun checkNumber(num: Number) {
    when (num) {
        is Int -> println("number is Int")
        is Double -> println("number is Double")
        else -> println("number not support")
    }
}

上述代码中,is关键字就是类型匹配的核心,它相当于Java中的instanceof关键字。

由于checkNumber()函数接收一个Number类型的参数,这是Kotlin内置的一个抽象类,像Int、Long、Float、Double等与数字相关的类都是它的子类,所以就里就可以使用类型匹配来判断传入的参数到底属于什么类型,如果是Int型或Double型,就将该类型打印出来,否则就打印不支持该参数的类型。

when语句还有一种不带参数的用法,虽然这种用法可能不太常用,但有的时候却能发挥很强的扩展性。

拿刚才的getScore()函数举例,如果我们不在when语句中传入参数的话,还可以这么写:

代码语言:txt复制
fun getScore(name: String) = when {
    name == "Tom" -> 86
    name == "Jim" -> 77
    name == "Jack" -> 95
    name == "Lily" -> 100
    else -> 0
}

可以看到,这种用法是将判断的表达式完整地写在when的结构体当中。注意,Kotlin中判断字符串或对象是否相等可以直接使用==关键字,而不用像Java那样调用equals()方法。可能你会觉得这种无参数的when语句写起来比较冗余,但有些场景必须使用这种写法才能实现。举个例子,假设所有名字以Tom开头的人,他的分数都是86分,这种场景如果用带参数的when语句来写就无法实现,而使用不带参数的when语句就可以这样写:

代码语言:txt复制
fun getScore(name: String) = when {
    name.startsWith("Tom") -> 86
    name == "Jim" -> 77
    name == "Jack" -> 95
    name == "Lily" -> 100
    else -> 0
}

现在不管你传入的名字是Tom还是Tommy,只要是以Tom开头的名字,他的分数就是86分。

5.3 For循环

Java中主要有两种循环语句:while循环和for循环。Kotlin的while循环和Java中的while循环没有任何区别。

Kotlin在for循环方面做了很大幅度的修改,Java中最常用的for-i循环在Kotlin中直接被舍弃了,而Java中另一种for-each循环则被Kotlin进行了大幅度的加强,变成了for-in循环,所以我们只需要学习for-in循环的用法就可以了。

区间的概念

闭区间:0, 10

代码语言:txt复制
val range = 0..10

左闭右开区间:[0,10):

代码语言:txt复制
val range = 0 until 10

有了区间之后,我们就可以通过for-in循环来遍历这个区间,比如在main()函数中编写如下代码:

代码语言:txt复制
fun main() {
    for (i in 0..10) {
        println(i)
    }
}

默认情况下,for-in循环每次执行循环时会在区间范围内递增1,相当于Java for-i循环中i 的效果,而如果你想跳过其中的一些元素,可以使用step关键字:

代码语言:txt复制
fun main() {
    for (i in 0 until 10 step 2) {
        println(i)
    }
}

上述代码表示在遍历[0, 10)这个区间的时候,每次执行循环都会在区间范围内递增2,相当于for-i循环中i = i 2的效果。

.. 和until关键字都要求区间的左端必须小于等于区间的右端,也就是这两种关键字创建的都是一个升序的区间。如果你想创建一个降序的区间,可以使用downTo关键字,用法如下:

代码语言:txt复制
fun main() {
    for (i in 10 downTo 1) {
        println(i)
    }
}

这里我们创建了一个10, 1的降序区间。

标注1:

函数式编程是一种编程范式,我们常见的编程范式有命令式编程(Imperative programming),函数式编程,逻辑式编程,常见的面向对象编程是也是一种命令式编程。更多了解推荐参考:https://www.zhihu.com/question/28292740

学习参考

1 官网文档 https://www.kotlincn.net/docs/reference/basic-syntax.html

2 郭霖:《第一行代码》

0 人点赞