kotlin的流畅性

一、关于运算符的重载

kotlin的运算符重载和c++的运算符重载比较类似,使用operator的方式: operator fun xxx的方式

比如重载类Complex的乘号

复制代码
data class Complex(val real: Int, val imaginary: Int) {
    operator fun times(other: Complex) =
    Complex(real * other.real - imaginary * other.imaginary,
            real * other.imaginary + imaginary * other.real)

    private fun sign() = if (imaginary < 0) "-" else "+"

    override fun toString() = "$real ${sign()} ${abs(imaginary)}i"
}

println(Complex(4, 2) * Complex(-3, 4))
println(Complex(1, 2) * Complex(-3, 4))
复制代码

如上所示,重载了两个Complex的乘法,使用times

具体关于各个运算符对应的重载方法名如下所示

如上所示,每个运算符均有对应的运算符重载方法;

那么可能有疑问了,为何kotlin不能完全像c++一样,加号就使用operator fun +() 而是使用了plus呢

是因为:JVM不支持运算符重载(而且kotlin编译成java字节码),kotlin通过将运算符映射到特殊命名的方法,使运算符重载成为可能。

 

二、使用扩展函数注入运算符的方式

1、扩展函数使用运算符

operator fun Pair<Int, Int>.plus(other: Pair<Int, Int>) =
Pair(first + other.first, second + other.second)

如上所述,虽然plus函数在Pair的类外面,但是我们在该位置可以使用加号对两个Pair进行相加

2、使用in运算符

复制代码
data class Point(val x: Int, val y: Int)
data class Circle(val cx: Int, val cy: Int, val radius: Int)

operator fun Circle.contains(point: Point) =
(point.x - cx) * (point.x - cx) + (point.y - cy)*(point.y - cy) < radius * radius

val circle = Circle(100, 100, 25)
val point1 = Point(110, 110)

println(circle.contains(point1))
println(point1 in circle)
复制代码

如上所示,通过增加operator标识符,使得contains可以使用in来表示,更像自然语言

如果打算使用中缀表达式,则可以使用infix 关键字:如下所示

infix fun Circle.contains(point: Point) =
(point.x - cx) * (point.x - cx) + (point.y - cy)*(point.y - cy) < radius * radius


println(circle contains point1)

如上所示,则是实用中缀的方式;如果要同时使用operator和infix也是可以的:operator infix fun Circle.contains(point: Point) = 。。。。。

 

 

三、关于四种方法的行为:let、also、run、apply

先总结各个方法的用法:

let:有一个context参数,并返回对应的计算结果

also:有一个context参数,并返回context参数

run:无参数,返回结果

apply:无参数,并返回context

》》个人理解:context即使用this,更细节的就不去深究了

 

例子,如下为了计算,只使用了from和send两个函数:

class Mailer {
    val details = StringBuilder()
    fun from(addr: String) = details.append("from $addr...\n")
   fun to(addr: String) = details.append("to $addr...\n") fun send()
= "...sending...\n$details" }

正常的调用逻辑:

val mailer = Mailer()
mailer.from("builder@111.com") // 先构造完,这里为了简单只使用from构造,其实还有to,body等构造
mailer.to("re@111.com") val result = mailer.send() println(result)

 

但是我们可以使用apply进行简化代码

val mailer1 = Mailer()
.apply{from("builder@111.com")}
.apply{to("re@111.com")}
val result1 = mailer1.send()
println(result1)

因为apply是无参,并且返回this,所以可以调用多个;我们也可以将上述的两个apply合并

val mailer1 = Mailer()
.apply {
from("builder@111.com")
to("re@111.com")
}

那么如果要将send也合并到一起呢? 因为send是返回String类型所以可以使用run的方式

val result2 = Mailer().run {
    from("builder@111.com")
    to("re@111.com")
    send()
}
println(result2)

如上,通过最后send返回的是String,返回给result2

 

同理,let和also也是差不多,只是let会将this作为参数传给lambda

 

四、关于隐式接收方

var length = 100
val printIt: (Int) -> Unit = {n: Int ->
    println("n is $n, length is $length")
}
printIt(6)

如上函数的打印结果是:n is 6, length is 100

但是我们如果要打印字符串的长度,则可以使用隐式的方式

val printIt: String.(Int) -> Unit = {n: Int ->
    println("n is $n, length is ${this.length}")
}

printIt("Hello", 6)
"Hello".printIt(6)

如上的两种方式均能否正常调用printIt函数,并且返回的结果是一样的

 

posted @   LCAC  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
点击右上角即可分享
微信分享提示