《Kotlin程序开发入门精要》之Kotlin基础知识

本章将详细讲解Kotlin中的一些基础知识,如第一行Kotlin代码应该如何写,以及Kotlin中常用的数据类型、包及控制流。这些内容在所有的Kotlin程序中都会用到。


本文摘自:《Kotlin程序开发入门精要》
购书: item.jd.com/12174925.ht…
试读: www.epubit.com.cn/book/detail…

2.1 Kotlin的基本语法

在开始深入讲解Kotlin语言之前,让我们先来熟悉一下Kotlin的基本语法。Kotlin的语法很复杂,我们一开始也不需要了解太多,只需要满足本章的学习需要即可。对于一种语言来说,变量和函数(方法)是最重要的两类语法。由于Kotlin语法糖的存在,让本不支持函数语法的JVM支持将函数放到代码的最顶层,因此,在本书中,就统一将Kotlin中的这类语法称为函数。本章的主要目的就是让读者学会定义变量和函数的基本方法。


2.1.1 定义变量


我们知道,绝大多数编程语言都有变量和常量的概念。例如,Kotlin和同源的Java类似,尽管没有常量语法,但可以使用final关键字定义一个不可修改的变量,其实就相当于常量。在Java中,无论是变量还是常量,数据类型和其他关键字都会放到变量名的前面。例如,下面的代码定义了2个Java变量和1个Java常量。


Java代码


int n = 30;             //  Java变量
int ok; // 仅定义了ok变量,并未初始化,需要在后期初始化
final int m = 20; // Java常量,不允许再次设置m
ok = 123; // 后期初始化ok变量
m = 10; // 编译出错,因为m是常量(final变量)复制代码

其中m在初始化完成后,是不允许再次修改的。


在Kotlin中,定义变量时有如下几个方面与Java不同。



  • 位置不同:在Kotlin中,数据类型要放到变量后面,并且用冒号(:)分隔,这一点正好和Java相反。
  • 变量和常量需要关键字:在Kotlin中,定义变量需要用var开头,定义常量需要用val开头。
  • 数据类型首字母:在Kotlin中,数据类型都以大写字母开头,而在Java中,一般只有类、接口等复杂数据类型的名字才采用首字母大写的方式。
  • 如果在定义变量时未进行初始化,就必须指定数据类型,如果在定义变量时进行了初始化,就可以不指定数据类型,Kotlin编译器会自动根据等号右侧的值推导数据类型。

下面利用Kotlin编写同样功能的代码。


Kotlin代码


var n : Int = 30        //  Kotlin变量,此处也可以不初始化,等待后期初始化
var ok: Int // 仅定义了ok变量,并未初始化,需要在后期初始化
val m: Int = 20 // Kotlin常量,不需要再次设置m
ok = 123 // 后期初始化ok变量
m = 10 // 编译出错,因为m是常量
var k = 100 // 自动推导变量k的数据类型复制代码

2.1.2 定义函数


无论是什么编程语言,函数的定义都分为如下几部分。



  • 函数头,包括函数名和标识函数的关键字,如fun、function等。
  • 参数,包括参数名和参数类型。
  • 返回值,包括返回值类型,以及如果没有返回值时的类型是什么。

现在让我们先回顾一下Java方法的定义。


Java代码


//  返回值类型是int
int add(int m, int n)
{
return m + n;
}
// 没有返回值,返回类型是void
void process(int m)
{
System.out.println(m m);
}
复制代码

从这段Java代码可以看出,函数头只是写了一个函数名,并没有任何标识,参数定义只是正常的Java变量的定义,如果没有返回值,返回类型是void。


对于Kotlin函数来说,与Java方法的定义还是有很大差异的,下面先看一段具备同样功能的Kotlin函数的定义代码。


Kotlin代码


fun add(m:Int, n:Int):Int
{
return m + n
}
fun process(m:Int): Unit
{
println(m 复制代码
m)
}复制代码


可以看到,定义Kotlin函数时,函数头除了包含函数名外,还必须包含fun关键字。至于参数,与定义Kotlin变量的方式相同。如果Kotlin函数有返回值,那么返回值类型在函数定义部分末尾指定,与函数定义部分用冒号(:)分隔。如果Kotlin函数没有返回值,那么可以返回Unit,也可以什么都不返回(省略Unit),代码如下:


Kotlin代码


fun process(m:Int)
{
println(m m)
}
复制代码


2.1.3 注释


对于一个复杂的程序,适当的注释是必要的。Kotlin中的注释与Java中的注释类似,也支持单行注释和块注释。


单行注释
// 这是一行注释

块注释
/复制代码
hello
world /复制代码

但Kotlin注释更强大一些,Kotlin块注释支持嵌套,代码如下:


Kotlin代码


/复制代码
hello
/ world /
/复制代码

这几行注释放到Java代码中会有编译错误,因为Java不支持块注释嵌套。


2.2 基础数据类型


在Kotlin中,一切都是对象。任何变量都有相应的方法和属性。不过为了提高执行效率,Kotlin对部分数据类型进行的优化,变成了内置的数据类型。不过这些类型的变量使用起来就像普通的类,同样有自己的方法和属性。本节主要介绍常用的数据类型,这些类型包括数值、字符、布尔类型和数组。


2.2.1 数值类型


Kotlin提供了如表2-1所示的内置数据类型。


表2-1 Kotlin提供的内置数据类型

数据类型

占用字节数

Double

8

Float

4

Long

8

Int

4

Short

2

Byte

1


下面的代码使用了这些内置类型定义了一些变量和常量。


Kotlin代码


var m: Int = 20
var price: Double = 20.4
var flag: Boolean = true
val v1: Int = 100
val v2: Double = 20.4复制代码

Kotlin的数值和Java的数值一样,也有默认类型。例如,整数的默认类型是Int,浮点数的默认类型是Double。不过在Java中,如果将占用字节数少的变量赋给占用字节数多的变量,会自动进行转换,代码如下:


Java代码


int m = 20;
byte n = 10;
m = n; // 将byte转换为int
long x = 20; // 将int转换为long
short value = 20;
m = value; // 将short转换为int复制代码

从这段Java代码可以看出, n是byte类型,value是short类型,n和value都赋给了m,m是int类型,因此,byte和short隐式地转换为int类型。下面再看看实现同样功能的Kotlin代码。


Kotlin代码


var m = 20
val n: Byte = 10
m = n // 编译错误,无法将byte隐式转换为int
val x: Long = 20 // 可以将int类型的值隐式转换为long类型
val value: Short = 20
m = value // 编译错误,无法将short隐式转换为int复制代码

如果非要将short类型转换为int类型呢?为了解决这个问题,Kotlin提供了一系列方法用来进行类型之间的转换。



toByte():转换到Byte类型。
toShort():转换到Short类型。
toInt(): Int:转换到Int类型。
toLong(): Long:转换到Long类型。
toFloat(): Float:转换到Float类型。
toDouble(): Double:转换到Double类型。
toChar(): Char:转换到Char类型。

前面给出的Java代码,如果使用这些转换方法,那么对应的Kotlin代码如下:


Kotlin代码


var m = 20
val n: Byte = 10
m = n.toInt() // 通过toInt方法将Byte转换为Int
val x: Long = 20
val value: Short = 20
m = value.toInt() // 通过toInt方法将Short转换为Int复制代码


Kotlin与Java一样,也提供了一些特殊表示法,用于表示Long和Float 类型的值,以及十六进制和二进制(八进制目前还不支持)。



表示Long类型的值,在数值后面加L或l,如123L。
表示Float类型的值,在数值后面加F或f,如123.1F。
表示十六进制,在数值前面加0x,如0x1F。
表示二进制,在数值前面加0b,如0b100101。

如果数值较大,并不容易马上确定值的大小,那么Kotlin提供了下画线(_)作为数值分隔符,可以根据需要对数值进行分隔。分隔符可以任意插在数值之间,如12345678,可以写成123_456_78,也可以写成1_2_3_4_5_6_7_8,至于写成什么样,可以根据数值具体表示什么来决定。


Kotlin代码


val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010复制代码

2.2.2 字符类型


在Kotlin语言中,字符类型用Char描述,不过和Java不同的是,在Kotlin中,字符不能直接看做是数字。例如,下面的Java代码,直接判断了char类型的ASCII。


Java代码


void check(char c)
{
if (c == 97)
{
// 可以编译通过
}
}复制代码

使用Kotlin代码实现同样的功能会出现编译错误。


Kotlin代码


fun check(c: Char)
{
// 会产生编译错误
if (c == 97)
{

}
}复制代码

与Java一样,Kotlin的字符也用单引号,代码如下:


fun check(c: Char)
{
if (c == 'a')
{
// 正常编译通过
}
}复制代码

Kotlin也支持反斜杠(\)转义符,常用的特殊字符如下:



\t:制表符
\b:退格符
\n:换行符
\r:回车符
\':单引号
\":双引号
\:反斜杠

虽然字符不能直接作为数值使用,但可以使用toInt方法将字符转换为相应的ASCII,也可以自定义一个函数,将数值字符转换为对应的数值。


Kotlin代码


//  将字符转换为数值,如'2'会转换为2
fun decimalDigitValue(c: Char): Int {
// 字符必须在'0'和'9'之间
if (c !in '0'..'9')
throw IllegalArgumentException("Out of range")
// 将当前指定的字符转换为对应的ASCII,然后再与'0'的ASCII相减,就可以将字符转换为对应的数值
return c.toInt() - '0'.toInt()
}复制代码

2.2.3 布尔类型


Kotlin语言中的布尔类型用Boolean描述,该类型有两个值:true和false。Boolean类型有3种操作:逻辑或(||)、逻辑与(&&)和逻辑非(!)。


下面的代码是使用Boolean类型的案例。


Kotlin代码


var flag1:Boolean = true
val flag2:Boolean = false
flag1 = false
if(flag1 && !flag2)
{
println("flag1 && !flag2")
}
if(!flag1 || flag2)
{
println("!flag1 || flag2")
}复制代码

2.2.4 数组


在Kotlin中,数组使用Array类描述,在该类中包含了get和set方法(通过操作符重载实现了[]操作,操作符重载在后面的章节会详细介绍),size属性以及其他很多有用的成员方法。


在Kotlin中,定义数组有多种方式,使用arrayOf函数定义可以存储任意值的数组;使用arrayOfNulls函数定义指定长度的空数组(数组元素中没有值);使用Array类的构造器指定数组长度和初始化数据的方式定义数组;使用intArrayOf、shortArrayOf等函数定义指定类型的数组,并初始化数组。下面是Kotlin数组的一些典型的例子。


Kotlin代码


//  使用arrayOf函数定义可以存储任意值的数组
val arr1 = arrayOf(1, 2, 3,'a')
println(arr1[3])
arr1[2] = 'b'
println(arr1[2])

// 使用arrayOfNulls函数定义数组
var arr2 = arrayOfNulls<Int>(10)
println("arr2的长度:" + arr2.size)

// 使用Array类的构造器定义数组,其中第二个参数是指初始化每一个数组元素的值
// 每个数组元素的值就是当前数组索引的乘积
val arr3 = Array(10, { i -> (i 复制代码
i).toString() })
println(arr3[3])

// 使用intArrayOf函数定义数组
var arr4:IntArray = intArrayOf(20,30,40,50,60)
println("arr4[2] = " + arr4[2])复制代码

2.2.5 字符串


在Kotlin中,使用String表示字符串类型,有如下两类字符串。



  • 普通字符串:这种字符串类似于Java,可以在字符串中加上转义符,如\n,会让转义符后面的字符串换到下一行,这种字符串需要放在双引号中。
  • 保留原始格式的字符串(raw string):这种字符串不能使用转义符,如果字符串中带有格式,如换行,直接写在字符串中即可。这种字符串需要放在3个引号对中。

下面是这两种字符串的演示代码。


//  第1类字符串(与Java类似)
var s1 = "hello \nworld"
var s2:String = "世界\n你好"
println(s1)
println(s2)

// 第2类字符串,保留原始格式
var s3 = """
hello
world
I love you.
"""

println(s3)复制代码

运行这段代码,会输出如图2-1所示的字符串。



▲图2-1 两种字符串的输出格式

可以看到,第1类字符串只是使用\n换了一行,而第2类字符串与源代码中输入的格式完全相同。


2.2.6 字符串模板


Kotlin字符串还有一项重要的功能,这就是字符串模板。所谓字符串模板,就是在字符串中添加若干个占位符,内容会在后期指定。也就是说,用模板可以设置字符串动态的部分。


模板使用美元符号($)设置,如"i = $i"中的$i就是一个占位符,其中$后面的i是变量,随着i的变化,"i = $i"中的值也随着变化。我们还可以使用任意表达式,不过要使用${表达式}语法。下面是在Kotlin字符串中使用模板的例子。


Kotlin代码


val i = 10
val s1 = "i = $i" // 相当于 "i = 10"
println(s1)

val s2 = "abc"
// 使用字符串的length属性获取s2的长度
val str = "$s2 的长度是 ${s2.length}"
println(str)复制代码

执行这段代码后,会输出如下内容。


i = 10


abc的长度是 3


2.3 包(Package)


用过Java的读者,对Package的概念肯定不会陌生。Package和C#中的命名空间类似,是为了尽可能避免类名重复而设计的。在Java中,Package和目录统一在了一起,也就是说,Package就是目录。例如,有一个Java类MyClass,该类的包是util.net.system,那么就意味着存在一个/util/net/system目录结构(如果是Windows,那么可能是D:\class\util\net\system),而MyClass.class或MyClass.java文件就在这个目录中。由于Java是跨平台的,因此包名各部分之间用点(.)分隔(因为Linux是用斜杠“/”分隔目录的,而Windows使用反斜杠“\”分隔目录)。


在Kotlin中,也存在包的概念,包在表达方式上与Java完全一样,不过Kotlin中的包和目录可没什么关系,Kotlin包仅仅是为了引用文件中的资源而设计的。例如,在下面的Kotlin代码中,定义了一个函数(process)和一个类(MyClass)。实际上,完整的函数名和类名分别是foo.bar.process和foo.bar.MyClass。


Kotlin代码


package foo.bar

fun process() {}

class MyClass {}复制代码

如果我们想引用其他Kotlin文件中的函数或类,该怎么办呢?现在让我们来做一个试验,首先建立两个Kotlin文件:MyKotlin.kt和Person.kt。首先,在Person.kt文件中输入如下的代码。


Kotlin代码(Person.kt)


package a.b
fun getName():String
{
return "Bill Gates"
}
class MyClass {}复制代码

很明显,Person.kt文件开始部分使用package关键字定义了a.b包,如果这个包定义在.java文件中,当前源代码文件必须在a/b目录中,而对于Kotlin来说,Person.kt可以在当前目录或任何其他的目录。在本例中,Person.kt和后面要建立的MyKotlin.kt文件在同一个目录,而MyKotlin.kt文件中并未定义任何包。


Kotlin代码(MyKotlin.kt文件)


fun main(args: Array<String>)
{
println(a.b.getName())
println(a.b.MyClass()) // 创建对象的实例,后面的章节会详细介绍
}复制代码

在MyKotlin.kt文件的main函数中,直接通过引用a.b包的方式调用了getName函数和MyClass类。


可能很多读者会发现一个问题,如果a.b包中的函数和类很多,岂不是每次都要加a.b.前缀了?关于这个问题,Java和Kotlin的解决方案一样,就是在代码的开始部分(package语句后面)使用import提前导入一些资源,如函数、类或Kotlin文件中的所有东西。


下面的代码分别导入了Person.kt中的getName函数和MyClass类。


Kotlin代码


import a.b.getName
import a.b.MyClass复制代码

当然,也可以像下面的代码一样,一次导入Person.kt中的所有资源。


Kotlin代码


import a.b.复制代码

如果按这种方式导入Person.kt中的资源,MyKotlin.kt文件的main函数就可以按下面的方式编写。


Kotlin代码(MyKotlin.kt文件)


fun main(args: Array<String>)
{
// getName和MyClass都不需要指定包
println(getName())
println(MyClass())
}复制代码

Kotlin中的import还提供了一个功能,就是给导入资源起一个别名。


Kotlin代码(MyKotlin.kt文件)


import a.b.getName as f
import a.b.MyClass as My复制代码

这段代码为getName函数起了一个称为f的别名,而MyClass的别名是My。如果资源拥有了别名,在当前文件(MyKotlin.kt)中,原来的资源名(getName和MyClass)就不可用了,因此,main函数需要改成如下形式。


Kotlin代码(MyKotlin.kt文件)


fun main(args: Array<String>)
{
println(f())
println(My())
}复制代码

我们会发现,在Kotlin中使用某些API是不需要import任何资源的,其实,Kotlin会默认导入一些包,这些包如下:



  • kotlin.

kotlin.annotation.
kotlin.collections.
kotlin.comparisons. (since 1.1)
kotlin.io.
kotlin.ranges.
kotlin.sequences.
kotlin.text.

以上导入的包都是Kotlin原生的API。还有一些与平台有关的API也会默认导入。


JVM默认导入的包如下:



  • java.lang.

kotlin.jvm.

JavaScript默认导入的包如下:


kotlin.js.


2.4 控制流


Kotlin的控制流与Java的控制流基本相同,只是使用when代替了switch。当然,在Kotlin中,if和when不仅仅可以作为语句使用,还可以作为表达式使用,这些内容会在本节详细讲解。


2.4.1 条件语句


在Kotlin中,if语句本身就是表达式,有返回值,因此,Kotlin并不需要像Java那样提供三元操作符(condition ?then: else)。下面是传统的if条件语句的用法。


Kotlin代码(传统的if语句用法)


var a:Int = 20
var b = 30
var max: Int
if (a < b) max = b

var min: Int
if (a > b) {
min = a
} else {
min = b
}复制代码

如果想将if语句作为表达式使用,那么可以按下面的代码形式编写if语句。如果if或else后面是代码块,那么最后一个表达式是返回值。


Kotlin代码(将if语句作为表达式使用)


var a = 20
var b = 30
val max = if (a > b) a else b
println(max)

// if else后面是一个代码块,最后一个表达式将作为返回值
val min = if (a > b) {
print("Choose a")
a // 返回值
} else {
print("Choose b")
b // 返回值
}复制代码

2.4.2 when语句


在Kotlin中,when替换了C语言风格的switch语句。标准的when语句的用法如下:


Kotlin代码(when作为语句使用)


var x = 1
when (x)
{
1 -> {
println("x == 1")
println("hello world")
}
2 -> print("x == 2")
else -> {
print("x is neither 1 nor 2")
}
}复制代码

使用when语句时,应注意以下几点。



when语句会根据传入的值(这里是x)寻找第一个满足条件的分支,找到后执行分支的语句。
如果分支中多于一条语句,要用{…}。
满足条件的分支执行后,会自动终止when语句的执行,因此,并不需要像switch语句那样每一个case语句都加上break。

when与if一样,既可以作为语句使用,也可以作为表达式使用。如果是后者,when语句的第一个满足条件的分支的最后一个表达式就是when表达式的返回值。


Kotlin代码(when作为表达式使用)


var x = 1
var m = when (x) {
1 -> {
println("x == 1")
20
}
2 -> {
print("x == 2")
60
}
else -> {
print("x is neither 1 nor 2")
40
}
}
println(m) // m的值是20复制代码

如果多个分支条件执行的代码都一样,可以在一个分支用逗号(,)分隔多个条件,代码如下:


Kotlin代码(多个分支执行相同的代码)


var x = 1
when (x) {
1,2 -> {
println("已经符合条件")
}
3 -> {
println("不符合条件")
}
else -> {
println("条件未知")
}
}复制代码

如果要执行相同代码的条件比较多,或无法枚举,可以使用in关键字确定一个范围,代码如下:


Kotlin代码(使用in关键字)


var n = 25
when(n)
{
in 1..10 ->println("满足条件")
in 11.. 20 ->println("不满足条件")
!in 30..60 ->println("hello world") // !in表示不在这个范围内
else->println("条件未知")
}复制代码

其实,when中的分支条件不仅可以是常量,还可以是任意表达式。例如,下面的代码分支条件就是一个函数。


Kotlin代码(分支条件是函数)


fun getValue(x:Int):Int
{
return x 复制代码
x
}

fun main(args: Array<String>)
{
var n = 4
when(n)
{
getValue(2)->println("满足条件")
getValue(3)->println("不满足条件")
else->println("条件未知")
}
}复制代码

2.4.3 for循环


在Kotlin中,for循环可以直接枚举集合中的元素,也可以按集合索引来枚举元素。下面的语法是使用迭代器(iterator)枚举集合中的所有元素。


Kotlin代码(for-iterator语法)


for (item in collection) print(item)复制代码

下面的代码使用这种方式枚举了数组中的所有元素值。


Kotlin代码(枚举数组中所有的元素值)


var arr = intArrayOf(2,4,6,8,10)
for (item: Int in arr) {
println(item)
}复制代码

下面的代码使用索引枚举数组中的元素值。


Kotlin代码


var arr = intArrayOf(2, 4, 6, 8, 10)
for (i in arr.indices) {
println("arr[$i] = " + arr[i])
}复制代码

执行这段代码,会输出如下结果。


arr[0] = 2


arr[1] = 4


arr[2] = 6


arr[3] = 8


arr[4] = 10


其实要想输出上面的结果,还有更简单的写法,就是在循环时,同时对索引和元素值进行循环,代码如下:


var arr = intArrayOf(2, 4, 6, 8, 10)
// index是索引,value是当前的数组元素值
for ((index, value) in arr.withIndex()) {
println("arr[$index] = " + value)
}复制代码

2.4.4 while循环


Kotlin中的while循环和Java中的while循环是一样的,也分为while和do…while,代码如下:


Kotlin代码(while和do…while循环)


var i= 0
while(i++ < 10)
{
println(i)
}

do
{
if(i == 6)
continue
println(i)
if(i == 5)
break
}while(--i > 0)复制代码

在do…while循环中使用了continue和break,这两个语句在for循环中同样可以使用。continue是为了忽略当前循环continue后面的所有语句,继续从下一次循环开始。break是为了终结当前循环,并跳出循环。这一点和Java完全一样。


2.5 小结


尽管编程语言之间是相通的,不过由于Kotlin中加入了很多语法糖,因此要想充分掌握Kotlin语言,还需要下一番功夫,而本章的目的就是让你练好Kotlin的基本功。

                </div>复制代码
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值