本文是对<<Kotlin in Action>>
的学习笔记,若是须要运行相应的代码能够访问在线环境 try.kotlinlang.org,这部分的思惟导图为: java
Kotlin
中,咱们能够经过
调用本身代码中定义的函数,来实现
特定语言结构。这些功能与
特定的函数命名 相关,而不是与特定的类型绑定。例如,若是在你的类中定义了一个名为
plus
的特殊方法,那么按照约定,就能够在该类的实例上使用
+
运算符,这种技术称为
约定。
由于由类实现的接口集是固定的,而Kotlin
不能为了实现其余接口而修改现有的类,所以通常 经过扩展函数的机制 来为现有的类增添新的 约定方法,从而适应任何现有的Java
类。算法
在Kotlin
中,使用约定的最直接的例子就是 算术运算符,在Java
中,全套的算术运算符只能用于基本数据类型,+
运算符能够与String
一块儿使用。下面,咱们看一下在Kotlin
中,如何使用算术运算符来完成一些其它的事情。数组
假设已经有一个数据类Point
,它包含两个成员变量,分别是x,y
点的坐标值,咱们但愿经过算术运算符+
对两个Point
对象相加以后,可以获得一个新的Point
对象,它的成员变量x,y
为原有两个Point
对象的x,y
之和。 ide
Point
类定义了一个扩展函数
plus
,这样当咱们调用
first + second
,实际上执行的是
first.plus(second)
方法来获得一个新的
Point
对象。这里须要注意的是:用于重载运算符的全部函数都须要
用 operator 关键字来标记,用来表示你打算 把这个函数做为相应的约定的实现。
全部可重载的二元算术运算符以下,自定义类型的运算符,基本上和标准数字类型的运算符有着相同的优先级。函数
a * b
:times
a / b
:div
a % b
:mod
a + b
:plus
a - b
:minus
Java
调用Kotlin
运算符很是容易,只须要像普通函数同样调用便可,例如上面的plus
方法。Kotlin
调用Java
的时候,对于与Kotlin
约定匹配的函数(不要求使用operator
修饰符,可是参数须要匹配名称和数量)均可以使用运算符语言来调用。若是Java
类定义了一个知足需求的函数,可是起了一个不一样的名称,能够经过定义一个扩展函数来修正这个函数名用来替代现有的Java
方法。Kotlin
没有为标准数字类型Int
,Long
等定义任何位运算符,所以也不容许你为自定类型定义它们。相反,它使用中缀调用语法的函数,能够为自定义类型定义类似的函数,下面咱们为Point
添加一个and
,用于执行位运算。 学习
operator
关键字来声明,而是用
infix
来定义一个中缀调用语法的函数,其它执行位运算的函数包括:
shl
、
shr
、
ushr
、
and
、
or
、
xor
和
inv
。
当在定义像plus
这样的函数,Kotlin
不止支持+
号运算,也支持像+=
这样的 复合赋值运算符。 spa
first
要声明为
var
。在一些状况下,定义
+=
运算符能够
修改使用它的变量所引用的对象,但不会从新分配引用,将一个元素添加到可变集合,就是一个很好的例子:
Unit
,名为
plusAssign
的函数,
Kotlin
将会在用到
+=
运算符的地方使用它,其它二元运算符也有命名类似的对应函数:
minusAssign
、
timesAssign
等。
当在代码中用到+=
的时候,理论上plus
和plusAssign
均可能会被调用,若是两个函数都有定义而且适用,那么编译器就会报错,例以下面这样的定义: 3d
first
,这样plus
运算符就再也不适用。plus
和plusAssign
运算。若是一个类是 不可变的,那就应该只提供返回一个新值的运算;若是一个类是 可变的,例如构建器,那么只须要提供plusAssign
和相似的运算符就够了。Kotlin
的标准库支持集合的这两种方法:code
+
和-
运算符老是返回一个新的集合+=
和-=
运算符用于可变集合时,始终在一个地方修改它们;而它们用于只读集合时,会返回一个修改过的副本。做为它们的运算数,可使用单个元素,也可使用元素类型一致的其它集合: component
重载一元运算的过程和前面看到的方式相同:用预先定义的一个名称来声明函数,并用修饰符operator
标记。下面的例子中重载了-a
运算符:
+a
:unaryPlus
-a
:unaryMinus
!a
:not
++a/a++
:inc
--a/a--
:dec
当你定义inc
和dec
函数来重载自增和自减的运算符时,编译器自动支持与普通数字类型的前缀、后缀自增运算符相同的语义。例如后缀运算会先返回变量的值,而后才执行++
操做。
与算术运算符同样,在Kotlin
中,能够对任何对象使用比较运算符(==
、!=
、>
和<
),而不只仅限于基本数据类型。
若是在Kotlin
中使用==/!=
运算符,它将被转换成equals
方法的调用,和其余运算符不一样的是,==
和!=
能够用于可空运算数,比较a == b
会检查a
是否为飞空,若是不是就调用a.equals(b)
,完整的调用以下所示:
a?.equals(b) ?: (b == null)
复制代码
对于data
修饰的数据类,equals
的实现将会由编译器自动生成,若是须要手动实现,能够参考下面的作法:
true
false
equals
函数之因此被标记为override
,这是由于这个方法的实现是在Any
类中定义的,而operator
关键字在基本方法中已经标记了。同时,equals
不能实现为扩展函数,由于继承自Any
类的实现始终优先于扩展函数。
在Kotlin
中,对于实现了Comparable
接口中定义的compareTo
方法的类能够按约定调用,比较运算符<、>、<=、>=
的使用将被转换为compareTo
,compareTo
的返回类型必须为int
,也就是说p1 < p2
表达式等价于p1.compareTo(p2) < 0
。
下面,咱们定义一个Person
类,让其根据年龄来比较大小:
Kotlin
标准库函数中的
compareValuesBy
函数来简洁地实现
compareTo
方法,这个函数
接收用来计算比较值的一系列回调,按顺序依次调用回调方法,两两一组分别作比较:
0
这些回调函数能够像lambda
同样传递,或者像这里作的同样,做为属性引用传递。
处理集合最多见的操做包含两种:
a[b]
,称为 下标运算符。in
运算符。在Kotlin
中,下标运算符是一种约定,使用下标运算符读取元素会被转换为get
运算符方法的调用,而且写入元素将调用set
,下面咱们为Point
类添加相似的方法:
get
的参数能够是任何类型,而不止是
Int
,例如,当你对
map
使用下标运算符时,参数类型是键的类型,它能够是任意类型。还能够定义具备多个参数的
get
方法,例如若是要实现一个类来表示二维数组或矩阵,你能够定义一个方法,例如
operator fun get(rowIndex : Int, colIndex : Int)
,而后用
matrix[row, col]
来调用。
下面,咱们再来看一下set
的约定方法:
set
函数后,就能够在赋值语句中使用下标运算符,
set
的最后一个参数用来接收赋值语句中(等号)右边的值,其余参数做为方括号内的下标。
集合支持的另外一个运算符是in
运算符,用于检查某个对象是否属于集合,相应的函数叫作contains
,下面的例子用于判断某个点是否处于矩形范围以内:
要建立一个区间时,使用的是..
语法,例如1..10
表明全部从1
到10
的数字,..
运算符是调用rangeTo
函数的一个简洁方法。rangeTo
返回一个区间,你能够为本身的类定义这个运算符,可是,若是该类实现了Comparable
接口,那么就不须要了,你能够经过Kotlin
标准库建立一个任意可比较元素的区间,这个库定义了能够用于任何可比较元素的rangeTo
函数
operator fun <T : Comparable<T>> T.rangeTo(that : T) : ClosedRange<T>
复制代码
这个函数返回一个区间ClosedRanged
,能够用来检测其它一些元素是否属于它。
做为例子,咱们用LocalData
来构建一个日期的区间:
now..now.plusDays(10)
将会被编译器转换为
now.rangeTo(now.plusDays(10))
,它并非
LocalDate
的成员函数,而是
Comparable
的一个扩展函数。
在for
循环中使用in
运算符表示 执行迭代操做,诸如for(x in list) { }
将被转换成list.iterator()
的调用,而后在上面重复调用hasNext
和next
方法。
object
来实现匿名内部类的知识。
解构声明的功能容许你展开单个复合值,并使用它来初始化多个单独的变量。它再次用到了约定的原理,要在解构声明中初始化每一个变量,将调用名为componentN
的函数,其中N
是声明中变量的位置。
对于数据类,编译器为每一个在主构造方法中声明的属性生成一个componentN
函数,下面的例子显示了如何手动为非数据类声明这些功能:
解构声明不只能够用做函数中的顶层语句,还能够用在其余能够声明变量的地方,例如使用in
循环来枚举map
中的条目: