摘要:變量聯(lián)動(dòng)指的是一個(gè)變量發(fā)生變化,其他變量也會(huì)發(fā)生變化。用Kotlin可以在語言層面實(shí)現(xiàn)變量聯(lián)動(dòng)。
關(guān)鍵詞:變量聯(lián)動(dòng);Kotlin;Getter;Setter
中圖分類號(hào):TP311 文獻(xiàn)標(biāo)識(shí)碼:A
文章編號(hào):1009-3044(2020)29-0214-03
1 背景
在編寫程序的過程中,為了邏輯上的統(tǒng)一,經(jīng)常需要一個(gè)變量的值發(fā)生變化,另一個(gè)變量的值也要產(chǎn)生變化,反之亦然。這兩個(gè)變量就屬于聯(lián)動(dòng)關(guān)系。
很多語言難以從語言層面實(shí)現(xiàn)這個(gè)功能,只能每次改變其中一個(gè)變量值的時(shí)候同時(shí)用代碼改變另一個(gè)變量值,這樣就顯得很噦唆。而Kotlin作為現(xiàn)代語言,就可以在語言層面實(shí)現(xiàn)這個(gè)功能。
2 變量聯(lián)動(dòng)
編程時(shí)經(jīng)常一個(gè)變量的值發(fā)生變化,另一個(gè)變量的值也要變化。比如有三個(gè)變量表達(dá)一個(gè)人的姓、名和姓名,當(dāng)姓發(fā)生變化時(shí),姓名也要發(fā)生變化:
String xing,ming,xingming;
xmg=”張”;
xmgmmg=xing+ming;
如果更改過于頻繁,可以把這兩條語句提煉成一個(gè)方法,更改姓的時(shí)候同時(shí)更改姓名,然后在需要更改姓的時(shí)候調(diào)用這個(gè)方法就可以了。
然而更改名的時(shí)候也要更改姓名,那么就需要另一個(gè)方法在更改名的時(shí)候同時(shí)更改姓名。如果直接更改姓名,還需要一個(gè)方法在更改姓名的時(shí)候把姓名分割,然后更改姓和名兩個(gè)變量。
這樣就需要3個(gè)方法來實(shí)現(xiàn)變量聯(lián)動(dòng),顯得很煩瑣。而Kotlin就可以很優(yōu)雅地實(shí)現(xiàn)這個(gè)功能。
3 Kotlin簡介
Kotlin由JetBrains公司開發(fā),如今Kotlin已經(jīng)支持Android開發(fā),并提供了一系列的Android開發(fā)插件。它是一門靜態(tài)語言,支持多種平臺(tái),包括移動(dòng)端、服務(wù)端以及瀏覽器端,此外,Kotlin還是一門融合了面向?qū)ο笈c函數(shù)式編程的語言,支持泛型、安全的空判斷,并且Kotlin與Java可以做到完全的交互。
4 Getter與Setter
Kotlin可以通過Getter在讀取變量時(shí)做出干預(yù)。如:
var test:String=””
get0=”aaa”
其中g(shù)et()就是Getter,讀取test變量所得到的是這個(gè)方法的返回值。那么
fun main(args:Array
test="123"
print(test)
)
的結(jié)果是輸出aaa,雖然test的真正值是123。
同樣,Kotlin可以通過Setter在給變量賦值時(shí)做出干預(yù)。如:
var test:String2””
set(value){field=”aaa'}
其中set0就是Setter,value是被賦的值,field稱為后備字段,它代表test真正的值。那么
fun main(args:Array
test="123"
print(test)
)
的結(jié)果是輸出aaa,雖然感覺上test被賦值成123,但在Set-ter中field被賦值成aaa,那么test的值就是aaa。
Setter經(jīng)常被用于預(yù)處理所賦的值,如表達(dá)分?jǐn)?shù)的值不能是負(fù)數(shù):
var score=0
set(value){field= if(value <0)0 else value}
fun main(args: Array
score=8
print(score)
score=-5
print(score)
}
的結(jié)果是80,雖然score第二次被賦值成-5,但在set方法中由于value的值是-5,小于0,然后field被賦值成0,所以score的真正的值是0。
需要注意的是在Getter與Setter中必須用field,不能用變量名,否則會(huì)陷入死循環(huán)。另外因?yàn)関al修飾的變量是只讀變量,所以val變量不能有Setter。
5 用Getter或Setter實(shí)現(xiàn)簡單的變量聯(lián)動(dòng)
如果要讓變量a2是變量a的兩倍,可以用如下代碼實(shí)現(xiàn):
classtf
var a=0
var a2=0
getO=a*2
)
fun main(args: Array
var x=t0
x.a=4
print(x.a2)
)
會(huì)輸出8,保證了a2是a的二倍。但如果給a2賦值,a的值卻不會(huì)自動(dòng)變成a2的一半。
也可以用Setter處理這個(gè)問題,代碼如下:
classtf
var a=0
set(value){
field=value
a2=a*2
)
var a2=0
)
同樣,如果給a2賦值,a的值不會(huì)自動(dòng)變成a2的一半。
6 用Setter實(shí)現(xiàn)姓名變量聯(lián)動(dòng)
對(duì)姓名問題而言,可以這樣寫xmg和nung的Setter:
class xmf
var xing:String=””
set(value){
field=value
xingming=value+ming
)
var ming:String= " "
set(value) [
field=value
xingming=xing+value
)
var xingming:String=””
)
fun main(args: Array
var x=xm0
x.xing=”張
x.rmng=”強(qiáng)”
print(x.xingming)
)
輸出的結(jié)果是張強(qiáng),雖然沒有明確給xmgming賦值,但在xing和rmng的Setter中卻給xingming賦值了,這樣就保證了3個(gè)變量的邏輯上的一致性。
對(duì)于xmgmmg的Setter,稍顯復(fù)雜,因?yàn)樗oxmg和mmg兩個(gè)變量賦值:
var xingming:String=
set(value) {
field=value
xing=value.substring(0,1)
ming=value.substring(l,value.length)
)
這樣的話
fun main(args: Array
var x=xm()
x.xingming=”張強(qiáng)”
print(x.xing+”一一”+x.ming)
)
的結(jié)果就是張一強(qiáng),把xing和mmg分開了。
需要注意的是,xingming的Setter不能和xing或ming的Set-ter -同出現(xiàn),因?yàn)樵趚ingming的Setter中會(huì)修改xing/ming,從而調(diào)用xing/ming的Setter,而xing/ming的Setter中會(huì)修改xing-rmng,從而調(diào)用xmgming的Setter,這樣就會(huì)陷入死循環(huán)。
所以這不是一個(gè)完美的方案。
7 用Getter實(shí)現(xiàn)姓名變量聯(lián)動(dòng)
對(duì)姓名問題而言,可以這樣寫xmgming的Getter:
class xmf
var xing:String="
var ming:String=“”
var xingming:String=””
getO=xing+ming
)
這樣
fun main(args: Array
var x=xm0
x.xmg=”張”
x.ming=”強(qiáng)”
print(x.xingming)
)
的結(jié)果是張強(qiáng),實(shí)現(xiàn)了變量聯(lián)動(dòng)。
xing和rmng的Getter可以這樣寫:
class xmf
var xing:String=””
getO=xingming.substring(0,1)
var ming:String=””
getO=xingming.substring(l,xingming.length)
var xingming:String=””
)
fun main(args: Array
var x=xm()
x.xlrWnmg=”張強(qiáng)”
print(x.xing+”一一”+x.ming)
)
結(jié)果是張一強(qiáng),把xing和rmng分開了。
但需要注意的是,和Setter -樣,xingming的Getter不能和xmg或rmng的Getter -同出現(xiàn),理由也類似:會(huì)造成死循環(huán)。
8 混用Getter和Setter實(shí)現(xiàn)姓名變量聯(lián)動(dòng)
單純用Getter或Setter都無法完美實(shí)現(xiàn)姓名變量聯(lián)動(dòng),混用卻可以實(shí)現(xiàn):
class xmf
var xing:String=…
var ming:String=””
var xingming:String="
getO=xing+ming
set(value){
field=value
xing=value.substring(0,1)
ming=value.substring(l,value.length)
)
)
通過只使用xmgrmng的Getter和Setter就可以完美實(shí)現(xiàn)姓名變量的聯(lián)動(dòng)。
fun main(args: Array
var x=xmQ
x.xmgrning=”張強(qiáng)”
print(x.xing+”一一”+x.ming)
x.xmg=”王”
print(x.xingming)
}
會(huì)輸出張一一強(qiáng)一王強(qiáng)。
另一種方法是使用xmg和ming的Getter和Setter實(shí)現(xiàn)姓名變量的聯(lián)動(dòng),原理類似,但更加復(fù)雜。
9 并聯(lián)電阻的電壓電流問題
電壓、電流、電阻之間也有聯(lián)動(dòng)關(guān)系,具體地說是電壓=電流×電阻。假設(shè)現(xiàn)在有兩個(gè)電阻并聯(lián),需要解決的是兩個(gè)電阻值和總電流、總電壓的關(guān)系。這里假設(shè)電流不可主動(dòng)賦值。
用Setter解決問題的代碼如下:
var U:Double=0.0
set(v){field=v;I=U/R1+U/R2)
var Rl:Double=l.0
set(v){field=v;I=U/R1+U/R2)
var R2:Double=l.0
set(v){field=v;I=U/R1+U/R2)
var I:Double=0.0
可以看出無論改變R1、R2、U中的哪一個(gè),都會(huì)重新計(jì)算電流的值。
fun main(args:Array
U=4.0
R1=2.0
R2=1.0
print(I.toString0+”一一”)
U=6.0
print(l)
)
的結(jié)果是6.0-9.0,解決了變量聯(lián)動(dòng)問題。
用Getter解決問題的代碼如下:
var U:Double=0.0
var Rl:Double=0.0
var R2:Double=0.0
val R:Double
getO=U/I
val Il:Double
getO=U/R1
val I2:Double
getO=U/R2
val I:Double
get0=11+12
可以看出,Getter方式可以求出更多的東西:RI的電流II、R2的電流12以及總電阻R,如果用Setter方式求這些,則要多出很多代碼。
10 結(jié)束語
用Kotlin的Getter和Setter可以優(yōu)雅地解決變量聯(lián)動(dòng)問題,在實(shí)戰(zhàn)的時(shí)候需要注意變量的Getter和Setter的死循環(huán)問題,另外在絕大多數(shù)場合,Getter比Setter更加方便。
參考文獻(xiàn):
[1]水滴技術(shù)團(tuán)隊(duì).Kotlin核心編程[M].北京:機(jī)械工業(yè)出版社,2019.
[2]封亞飛.揭秘Kotlin編程原理[M].北京:電子工業(yè)出版社,2018.
[3] Saumont P-Y.Kotlin編程之美.機(jī)械工業(yè)出版社[M].北京:機(jī)械工業(yè)出版社,2020.
【通聯(lián)編輯:謝媛媛】
作者簡介:劉華煜(1976-),男,講師,碩士,研究方向?yàn)橛?jì)算機(jī)應(yīng)用。