前言編程
在寫自定義控件的時候,有時會須要對PointF對象進行必定操做,計算兩個點之間的水平間距和垂直間距。app
簡化需求也就是要算出兩個點之間的差值。函數
用代碼實現大概是這樣的優化
fun minusPoint(p1: PointF, p2: PointF): PointF {
val dx = p1.x - p2.x
val dy = p1.y - p2.y
return PointF(dx, dy)
}
//使用
val p = minusPoint(p1,p2)
複製代碼
第一次修改this
這樣的寫法太Java了,由於咱們用到的是Kotlin,咱們能夠改爲這樣spa
fun minusPoint(p1: PointF, p2: PointF): PointF = PointF(p1.x - p2.x, p1.y - p2.y)
//使用
val p = minusPoint(p1,p2)
複製代碼
第二次修改code
固然,這樣也不夠好。咱們使用Kotlin的擴展函數爲PointF這個對象添加上一個擴展函數對象
fun PointF.minusPoint(p2: PointF): PointF = PointF(this.x - p2.x, this.y - p2.y)
//使用
val p = p1.minusPoint(p2)
複製代碼
這樣的調用看起來可讀性高了很是多。ci
第三次修改io
由於PointF自帶了offset的方法
public final void offset(float dx, float dy) {
x += dx;
y += dy;
}
複製代碼
因此咱們將能夠改爲這個樣子
fun PointF.minusPoint(p2: PointF): PointF = PointF().apply {
this.offset(-p2.x, -p2.y)
}
//使用
val p = p1.minusPoint(p2)
複製代碼
第四次修改
有編程經驗的小夥伴可能從第一次就發現了這個函數的一個「問題」,就是每次都會建立一個新的PointF對象。因此咱們還能夠對它進行一次「優化」
fun PointF.minusPoint(p2: PointF): PointF = this.apply {
this.offset(-p2.x, -p2.y)
}
//使用
val p = p1.minusPoint(p2)
複製代碼
這樣每次調用都不會產生新的對象,直接使用原來的對象就能夠了。一切都看起來很美妙。
第五次修改
咱們再次回到咱們一開始的時候,咱們一開始須要解決的問題是「計算兩個點的差值」,那麼從語義上來說。是否是能夠簡單的描述成爲這樣
val p1: Point
val p2: Point
val p = p1 - p2
複製代碼
瞭解Kotlin 的operator的同窗可能從第一次看到需求的時候就想到了-操做符。
很明顯 ktx中就有PointF的擴展操做符。
/**
* Offsets this point by the negation of the specified point and returns the result
* as a new point.
*/
inline operator fun PointF.minus(p: PointF): PointF {
return PointF(x, y).apply {
offset(-p.x, -p.y)
}
}
//使用
val p = p1 - p2
複製代碼
再一次被Kotlin 甜到 !
第六次修改
細心的朋友發現,這個擴展操做符每次都返回了一個新的對象。
那是否是ktx這個函數寫的很差?
其實不是這樣的,如今回到咱們的第四次的「優化」。
fun PointF.minusPoint(p2: PointF): PointF = this.apply {
this.offset(-p2.x, -p2.y)
}
//使用
val p = p1.minusPoint(p2)
複製代碼
如今咱們來考慮一個問題,咱們使用了p1對象減去p2的得到了一個對象p ,這時p其實就是p1,而它們的屬性此時已被改變。若是這時,再去使用p1去作一些其餘操做,顯然就和預期獲得的結果不同了。
發現問題所在了嗎?咱們的優化「減法」改變被減數,這顯然是不合理的。
因此咱們在第五次的修改是不太合理的,可是我又不想用第六次的方案,由於它的確額外的對象,我就是餓死,死在外面,也不會吃這個語法糖的?!。
那麼應該怎麼辦呢?
上面咱們說到,一個減法是不該該去改變被減數的,減法獲得的值理所固然是一個新的值。
那麼是否咱們就只能這樣了呢?固然不是,咱們再次回到咱們的需求,「得到兩個點之間的差值」,其實這句需求還能夠再增長完善一些,「得到兩個點之間的差值,爲了避免產生新的對象能夠直接修改其中一個點的值」
那麼到這裏能夠發現,咱們有一個很是合適的操做符來描述它,也就是 -=
直接來上代碼
inline operator fun PointF.minusAssign(p: PointF) {
this.apply {
offset(-p.x, -p.y)
}
}
//使用,沒有返回值
p -= p2
複製代碼
btw,因爲傳入的參數不是函數類型,這裏的inline是多餘的。
因爲沒有返回值,那麼咱們能夠這樣調用
val p1 = p.apply {
this -= center
}
複製代碼
至此,咱們的減法就算完成了。
經過這個減法,我獲得了什麼?