12

Kotlin 知识梳理(1) | 基础概念一览

 2 years ago
source link: http://www.androidchina.net/7709.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
基础概念一览 – Android开发中文站
你的位置:Android开发中文站 > Android开发 > Kotlin 知识梳理(1) | 基础概念一览

0.jpeg

作者:泽毛

链接:jianshu.com/p/f9e78d6c54bd

来源:简书

著作权归作者所有,已授权原创,禁止私自转载

本文是对《Kotlin in Action》的学习笔记,如果需要运行相应的代码可以访问在线环境 try.kotlinlang.org,这部分的思维导图为:

0.png

一、函数

01.函数的基本构成

在Kotlin中,函数的基本结构由四个部分构成:

函数的声明以关键字 fun 开始,函数名称紧随其后,接下来是括号括起来的参数列表,参数列表的后面跟着返回类型,返回类型和参数列表之间用冒号隔开,最后是函数体。

下面是一个比较大小的函数例子,上面谈到的四个部分构成如图中标注所示:

02. 表达式和语句

在上面的例子中,if是表达式,而不是语句。表达式和语句的区别在于:

  • 表达式:有值,并且能作为另一个表达式的一部分使用。
  • 语句:总是包含着它的代码块中的顶层元素,并且没有自己的值。

在Java中,所有的控制结构都是语句,而在Kotlin中,除了for、do和do/while以外大多数控制结构都是表达式。

当函数体是由单个表达式构成时,可以用这个表达式作为完整的函数体,并且去掉花括号和return语句,上面的例子就是这种情况,因此可以改写为:

  • 如果函数体写在花括号中,我们说这个函数有代码块体
  • 如果它直接返回了一个表达式,它就有表达式体
03. 省略返回类型

对于表达式体函数,可以省略返回类型,因为编译器会分析作为函数体的表达式,并把它的类型作为函数的返回类型,这种分析称为类型推导。但是对于有返回值的代码块体函数,必须显示地写出返回类型和return语句。

上面的例子可以简化为:

二、变量

在Kotlin中,变量的声明以关键字val/var开始,然后是变量名称,最后可以加上类型(不加也可以),这里分为两种情况:

  • 如果指定了初始化器,那么在不指定类型的情况下,编译器会分析初始化器表达式的值,并把它的类型作为变量的类型,例如下面两个就分别为Int和Double类型:
  • 如果没有指定初始化器,需要显示地指定它的类型,因为此时编译器无法推断出它的类型。
01. 可变变量和不可变变量

声明变量的关键字有两个:

(1) 不可变引用 val

使用val声明的变量不能在初始化之后再次赋值,它对应的是Java的final变量。

默认情况下,应该尽可能地使用val关键字来声明所有的Kotlin变量。在定义了val变量的代码块执行期间,val变量只能进行唯一一次初始化,但是,如果编译器能确保唯一一条初始化语句会被执行,可以根据条件使用不同的值来初始化它。

(2) 可变引用 var

这种变量的值可以改变,但是它的类型却是改变不了的。

如果需要在变量中存储不匹配类型的值,必须手动把值转换或强制转换到正确的类型。

02. 字符串模板

Kotlin可以在字符串字面值中引用局部变量,只需要在变量名称前面加上字符$。

如果要在字符串中使用$,需要对它进行转义。

运行结果为:

除了可以引用局部变量之外,还可以引用更加复杂的表达式,只需要把表达式用花括号扩起来。

三、类

类的概念就是把数据和处理数据的代码封装成一个单一的实体,在Java中,数据存储在字段中,并且通常是私有的。如果想让类的使用者访问到数据得提供访问方法,即getter/setter。

在Java中,字段和其访问器的组合常常被叫作属性。在Kotlin中,属性是头等的语言特性,完全 替代了字段和访问器方法。在类中声明一个属性和声明一个变量一样:使用val/var关键字,前者是只读的,而后者是可变的。

当声明属性的时候,就声明了对应的访问器(只读属性有一个gettter,而可变属性则有getter/setter),例如下面的例子,声明了只读的name属性,可变的isMarried属性,其赋值和读取的方法如下所示:

运行结果为:

02. 自定义访问器

假设声明一个矩形,它能判断自己是否是正方形,那么就不需要一个单独的字段来存储这个信息,此时我们可以写一个自定义的访问器:用val开头作为声明,紧跟着的是属性的名称和类型,接下来是get()关键字,最后是一个函数体。

运行结果为:

03. 目录和包

1. Kotlin中包的概念和Java类似,每个kotlin文件都能以一个package语句开头,而文件中定义的所有声明(类、函数及属性)都会被放到这个包中。

2. 如果其他文件定义的声明也有相同的包,这个文件可以直接使用它们;如果包不同,则需要导入它们,导入语句放在文件的最前面并使用import关键字。

3. kotlin不区分导入的是类还是函数,而且,它允许使用import关键字导入任何种类的声明,可以直接导入顶层函数的名称,也可以在包名称后加上.*来导入特定包中定义的所有声明。

4. 在Java中,要把类放到和包结构相匹配的文件与目录结构中,而在kotlin中,可以把多个package声明不相同的类放在同一个文件夹中。

四、表示和处理选择:枚举和when

01. 声明枚举类

(1)简单枚举类

声明枚举类时,enum是一个所谓的软关键字,只有当它出现在class前面时才有特殊的意义,在其他地方可以当做普通名称使用。而class仍然是一个关键字,下面是一个枚举类的声明:

(2)带属性的枚举类

以下是一个带属性的枚举类:

运行结果为:

当声明一个带属性的枚举类时,有几点需要注意:

  • 当声明每个枚举常量的时候,必须提供该常量的属性值。
  • 如果要在枚举类中定义任何方法,就要使用分号把枚举常量列表和方法分开。
02. 使用 “when” 处理枚举类

when是一个有返回值的表达式,因此,作为表达式函数体,它可以去掉花括号和return语句,并省略返回类型的声明。

下面是一个通过when处理枚举类的例子,它和Java中的switch语句类似,根据when中Color的值走到对应的分支,除此之外,我们可以把多个值用逗号间隔,合并到同一个分支:

运行的结果为:

03. 在 “when”结构中使用任意对象

在Java中,和when类似的switch语句要求必须使用常量(枚举常量、字符串或者数字字面值)作为 分支条件,而when允许使用任何对象,我们使用一个函数来混合两种颜色。

下面例子中用到的setOf是由Kotlin标准函数库提供的,它可以创建出一个Set,并且会包含所有指定为函数实参的对象,只要两个set中包含一样的条目,它们就是相等的,集合的条目顺序并不重要。

运行结果为:

除此之外,我们还可以不给when表达式提供参数,这样分支条件就是任意的布尔表达时,这种写法的优点是不会创建额外的对象,但代价是它更难理解。

04. 智能转换:合并类型检查和转换

在kotlin中,判断一个变量是否是某种类型需要使用is关键字,它和Java当中的instanceOf相似:

  • 在Java中,在检查完后还需要显示地加上类型转换。
  • 在kotlin中,如果你检查过一个变量是某种类型,后面就不需要再转换它,可以把它当做你检查过的类型来使用。

我们用下面这个例子,Num和Sum都实现了Expr接口,通过is判断它的类型,完成递归求和。可以看到,在is判断之后,不再需要转换成Num或Sum,就可以直接访问该类的成员变量。

运行结果为:

智能转换 只在变量经过is检查且之后不再发生变化 的情况下有效,当你对一个类的属性进行智能转换的时候,这个属性必须是一个val属性,而且不能有自定义的访问器,否则,每次对属性的访问是否都能返回同样的值将无从验证。

05. 代码块作为 “if” 和 “when” 的分支

if和when都可以使用代码块作为分支体,这种情况下,代码块中的最后一个表达式就是结果,这个规则在所有使用代码块并期望得到一个结果的地方成立。同样的规则对try主体和catch子句也有效。

运行结果为:

五、迭代事务

01. while 循环

kotlin和Java一样,有while循环和do-while循环,它们的语法和Java中相应的循环完全一致。

02. 迭代数字:区间和数列

在Java当中,对于循环的处理方式为:先初始化变量,在循环的每一步更新它的值,并在值满足某个限制条件时退出循环。

而在Kotlin中,为了替代常见的循环用法,使用了 区间 的概念,其本质上就是两个值之间的间隔,这两个值通常是数字:一个起始值,一个结束值。使用..运算符来表示区间,而结束值始终是区间的一部分。

运行结果为:

除此之外,还有downTo、step和until等用于区间的语法,用于进行循环操作,例如下面的例子downTo用于递减到指定的值,而step则指定步长:

运行结果为:

使用until则可以使迭代不包含指定的结束值,例如下面这样:

运行结果为:

03. 迭代 map

1. 更新 map

这里我们用到了TreeMap,在更新map时,我们可以像使用数组一样,只不过下标变成了key值:

2. 访问 map

下面的例子展示了for允许允许展开迭代中的集合的元素,把展开的结果存储到了两个独立的变量中:letter是键、binary是值:

运行结果为:

04. 使用 “in” 检查集合和区间的成员

使用in运算符来检查一个值是否在区间中,或者它的逆运算!in来检查这个值是否不在区间中,区间不仅限于字符,假如有一个支持实例比较操作的任意类(实现了java.lang.Comparable接口),就能创建这种类型的对象的区间。

运行结果为:

六、Kotlin 中的异常

kotlin的异常处理和Java以及其他许多语言的处理方式类似:一个函数可以正常结束,也可以在出现错误的情况下抛出异常。方法的调用者能捕获到这个异常并处理它;如果没有处理,异常会沿着调用栈再次抛出。

  • 抛出异常时使用throw关键字,但是不必使用new关键字来创建异常实例。
  • throw结构是一个表达式,能作为另一个表达式的一部分使用。

01. “try” “catch” 和 “finally”

当使用带有catch和finally子句的try结构来处理异常时,下面是一个典型的结构:

和Java最大区别就是throws子句没有出现在代码中:如果使用Java来写这个函数,你会显示地在函数声明上面写上throws IOException。这是因为IOException是一个受检异常,在Java中,这种异常必须显示地处理,必须声明你的函数能抛出所有的受检异常。

Java 处理方式

kotlin不区分受检异常和未受检异常,不必指定函数抛出的异常,而且可以处理也可以不处理异常。

与此同时,BufferReader.close可能抛出需要处理的受检异常,如果关闭失败,大多数程序不会采取什么有意义的行动,所以捕获来自close方法的异常所需的代码是多余的。

02. “try”作为表达式

kotlin中的try关键字就像if和when一样,引入了一个表达式,可以把它的值赋给一个变量,并且需要用花括号把语句主体括起来。如果主体包含多个表达式,那么整个try表达式的值就是最后一个表达式的值。

运行结果为:

转载请注明:Android开发中文站 » Kotlin 知识梳理(1) | 基础概念一览


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK