kotlin第六天:解构、运算符重载
解构声明
解构声明,允许你展开单个复合值,并且使用它来初始化多个单独的变量。
val p = Point(10,20)
val(x,y) = p
事实上,解构声明用到了约定的原理。要在解构声明中初始化每个变量,
将调用名为 componentN 的函数 ,其中 N 是声明中变量的位置.
上面的例子实际是:
val x = p.component1()
val y = p.component2()
那么要支持解构声明,对于类型有什么要求呢?
1、对于数据类,编译器为每个在主构造方法中声明的属性生成一个componentN函数
2、对于非数据类需要我们自己手动添加
class Point(val x :Int,val y:Int){
operator fun component1() = x
operator fun component2() = y
}
当然,不可能定义无限数量的 componentN 函数,这样这个语 法就可以与任 意数量的集合一 起工作,但这也没用 。标准库只 允许使用此语 法来访问一个对象的 前五个元素。
使用场景一:函数返回多个值
解构声明主要使用场景之一,是从一个函数返回多个值,这个非常有用。如果要这样做,可以定义一个数据类来保存返回所需的值,并将它作为函数的返回类型。在调用函数后,可以用解构声明的方式,来轻松地展开它,使用其中的值。
fun main(args: Array<String>) {
val (name,ext)= spliteFileName("example.kt")
println(name)
println(ext)
}
data class NameComponents(val name:String,val extension:String)
fun spliteFileName(fullName:String):NameComponents{
val result = fullName.split('.',limit = 2)
return NameComponents(result[0],result[1])
}
使用场景二:循环迭代
fun printMap(map:Map<String,String>){
for((key,value) in map){
println("key:$key,value:$value")
}
}
其实只要是定义变量的地方,就可以使用解构声明
运算符重载
重载二元算术运算
基本语法
先看一个例子:
data class Point(val x :Int,val y:Int){
operator fun plus(other:Point):Point{
return Point(x+other.x,y+other.y)
}
}
fun main(args: Array<String>) {
val p1 = Point(10,20)
val p2 = Point(30,40)
val p3= p1+p2
println(p3)
}
//输出
//Point(x=40, y=60)
这个是重载“+”运算符;
注意三点:
1、使用operator
关键字修饰plus函数;用于运算符重载的所有函数都必须用该关键字标记
2、由于重载的是“+”运算符,所以函数名叫plus;kotlin不支持自定义运算符,对于已有的运算符也有其对应的名字
表达式 | 函数名 |
---|---|
a*b | times |
a/b | div |
a%b | mod |
a+b | plus |
a-b | minus |
3、自定义运算符的优先级与标准数字型的运算符有着相同的优先级;即:*、/、%相同优先级,高于+、-优先级
运算符函数声明为扩展函数
除了把这个运算符声明为一个成员函数外, 也可以把它定义为一个扩展函数。
operator fun Point.plus(a:String):String{
return "$a x = $x,y = $y"
}
运算符函数重载
先看个例子
data class Point(val x :Int,val y:Int){
operator fun plus(other:Point):Point{
return Point(x+other.x,y+other.y)
}
}
fun main(args: Array<String>) {
val p1 = Point(10,20)
val p2 = Point(30,40)
val p3= p1+p2
println(p1+"测试")
println(p3)
}
operator fun Point.plus(a:String):String{
return "$a x = $x,y = $y"
}
这个例子体现了几点:
1、运算符函数可以重载
2、不要求两个运算数是相同的类型
3、返回类型可以是任意类型
4、Kotlin运算符不会自动支持交换性,因为有可能返回值类型和原类型都不一样
kotlin 没有位运算符,因此也不允许自定义类型定义他们。
kotlin提供了使用支持中级调用语法的常规函数 ,可以为自定义类型定义相似的函数
shl一一带符号左移
shr-一一带符号右移
ushr一一无符号右移
and一一按位与
or-一一按住或
xor一一按住异或
inv一一按住取反
重载复合赋值运算符
像+=、-=这样的运算符称为:复合赋值运算符
像重载了“+”运算符,如果返回值类型还是原类型,就可以使用+=运算符
var p1 = Point(10,20)
var p2 = Point(30,
p1 +=p2//等价 p1= p1+p2
在一些情况下,定义+=运算 可以修改使用它的变量所引用的对象,但不会重新分配引用
val numbers = ArrayList<Int> ()
numbers += 42
println(numbers[O])
//输出 42
如果你定义了一个返回值为 Unit,名为 plusAssign 的函数, Kotlin将会在 用到+=运算符的地方调用它。其他二元算术运算符也有相似的函数:minusAssign、 timesAssign 等 。
当你在代码中用到+=的时候,理论上plus和plusAssig口都可能被调用。如果在这种情况下,两个函数都有定义且适用,编译器会报错。
解决办法:
一种:可行的解决办法是,替换运算符的使用为普通函数调用。
另一个种:用 val 替 换 var,这样 plusAssign 运算就不再适用。
但一般来说,最好 一 致地设计出新的类: 尽量不要同时给一个类添加 plus 和 plusAssign 运算。如果像前面的 一个示例中 的 Point,这个类是不可变的,那么就应该只提供返回一个新值(如 plus)的运算。 如果一个类是可变的,比如构建器,那么只需要提供 plusAssign 和类似的运算 就够了。
重载一元运算符
一元运算符和与前面的运算符相似
表达式 | 函数名 |
---|---|
+a | unaryPlus |
-a | unaryMinus |
!a | not |
++a,a++ | inc |
–a,a– | dec |
重载比较运算符
比较运算符就是“==,!=,>,<,>=,<=”等符号
等号运算符
在kotlin中== 会转换成equals方法,!=也会转换成equals,只是会取反一下;
==和!= 可用与空运算;
a==b
//等价于
a?.equals(b)?:b==null
一point为例,看一下equals重写
class Point(val x :Int,val y:Int){
operator fun plus(other:Point):Point{
return Point(x+other.x,y+other.y)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Point
if (x != other.x) return false
if (y != other.y) return false
return true
}
}
1、equals不需要operator关键字修饰,增加了override修饰;这是因为这个方法在Any类中定义了;切在Any类中已经标记了operator关键字
2、恒等号=不能被重载,含义和java中的 一样,比较两个对象是不是同一个引用
3、不能把equals函数定义成扩展函数,因为会优先使用Any中的函数,所以就不会使用扩展函数
排序运算符
kotlin中也和java一样,实现Comparable 接口,只是java中实现了Comparable 接口不支持<,>,<=,>=等运算符,kotlin支持。
class Person(val firstName:String,val lastName:String) :Comparable<Person>{
override fun compareTo(other: Person): Int {
return compareValuesBy(this,other,Person1::firstName,Person1::lastName)
}
}
val p1=Person("a","b")
val p2=Person("c","d")
p1>=p2//等价于p1.compareTo(p2)>=0