Kotlin學(xué)習(xí)筆記(6)- 屬性

系列文章全部為本人的學(xué)習(xí)筆記,若有任何不妥之處炬称,隨時(shí)歡迎拍磚指正伞广。如果你覺得我的文章對你有用拣帽,歡迎關(guān)注我,我們一起學(xué)習(xí)進(jìn)步嚼锄!
Kotlin學(xué)習(xí)筆記(1)- 環(huán)境配置
Kotlin學(xué)習(xí)筆記(2)- 空安全
Kotlin學(xué)習(xí)筆記(3)- 語法
Kotlin學(xué)習(xí)筆記(4)- 流程控制
Kotlin學(xué)習(xí)筆記(5)- 類
Kotlin學(xué)習(xí)筆記(6)- 屬性
Kotlin學(xué)習(xí)筆記(7)- 接口
Kotlin學(xué)習(xí)筆記(8)- 擴(kuò)展
Kotlin學(xué)習(xí)筆記(8)- 擴(kuò)展(續(xù))
Kotlin學(xué)習(xí)筆記(9)- 數(shù)據(jù)類
Kotlin學(xué)習(xí)筆記(10)- 泛型
Kotlin學(xué)習(xí)筆記(11)- 內(nèi)部類和嵌套類
Kotlin學(xué)習(xí)筆記(12)- 委托
Kotlin學(xué)習(xí)筆記(13)- 函數(shù)式編程
Kotlin學(xué)習(xí)筆記(14)- lambda

一减拭、屬性聲明

一般的說,類是屬性和邏輯的集合区丑。我們用方法封裝和處理邏輯拧粪,用變量聲明屬性。所以可以大言不慚的說一句沧侥,屬性聲明在類中是必不可少的可霎。在《Kotlin學(xué)習(xí)筆記(3)- 語法》中我們介紹過,kotlin中的屬性聲明有兩種:var聲明普通變量宴杀,val聲明只讀變量(暨final類型)癣朗。其實(shí)還有幾點(diǎn)需要說明。

  1. 類中聲明的屬性旺罢,一定要初始化旷余,否則會編譯錯(cuò)誤绢记。除非你對屬性使用了abstract進(jìn)行修飾。

    var name: String = ""
    abstract var size : Int
    
  2. 屬性聲明中的屬性類型在大部分情況下是可以省略的正卧。這里說的大部分是指我們的最多的使用情況蠢熄,也就是默認(rèn)的使用場景。

    var name = ""
    

    那什么時(shí)候是不能省略的呢炉旷?這還要說到另一個(gè)問題护赊,那就是屬性的修飾符,和方法類似的砾跃,屬性也有多種修飾符骏啰。

    public  // 默認(rèn)的修飾符。全部可見抽高,在屬性被初始化時(shí)判耕,
            // 如果可以根據(jù)屬性的值推斷出屬性類型,則可省略類型
    protected // 在本身和子類中可見翘骂,如果可以根據(jù)屬性的值推斷出屬性類型壁熄,則可省略類型
    private  // 只在本身可見,如果可以根據(jù)屬性的值推斷出屬性類型碳竟,則可省略類型
    abstract  // 在自身不初始化草丧,需要子類進(jìn)行初始化,不能省略類型莹桅。
    
  3. 類中的屬性昌执,用.進(jìn)行訪問例如上面的name屬性,我們可以這樣進(jìn)行讀寫操作

    var person = Person()
    person.name = "jck"      // 寫
    Log.d("log", person.name)  // 讀
    

二诈泼、getter和setter

  1. 默認(rèn)方法

    上面的例子可以看到懂拾,我們可以直接調(diào)用person.name對屬性進(jìn)行讀寫操作,這種操作我們在java中也見過铐达,是對類中的public屬性進(jìn)行操作岖赋。而在kotlin中,name屬性雖然也是public的瓮孙,但是意義和java中是完全不同的唐断,這里的讀寫其實(shí)是對getset函數(shù)的隱式調(diào)用,而getset函數(shù)是默認(rèn)實(shí)現(xiàn)的杭抠,而顯式的寫出來則是這樣

    // 非源碼脸甘,而是根據(jù)自己理解寫出的。這里的field下面會說
    var name: String = ""
        get() = field            // get
        set(value) {         // set
            field = value
        }
    

    也就是說祈争,之前我們說到的person.name的讀寫操作斤程,其實(shí)是對getset方法的訪問角寸,而并不是像java中的直接對屬性的操作菩混,保證了屬性的閉合性忿墅。完整的聲明如下:

    var <propertyName>: <PropertyType> [= <property_initializer>]
    [<getter>]
    [<setter>]
    

    其中initializer, getter 和 setter都是可選的。var是允許有g(shù)etter 和 setter方法沮峡,val不允許有setter方法疚脐。如果屬性值的數(shù)據(jù)類型可以通過編譯器自動推斷,或者在getter和setter方法中并沒有對屬性做特殊處理邢疙,這些方法都可以省略棍弄。

  2. 訪問權(quán)限

    在java中我們可以根據(jù)需要決定是否實(shí)現(xiàn)屬性的getset方法,kotlin中自然也有針對針對這種需求的實(shí)現(xiàn)疟游。首先屬性都是有getset方法的呼畸,當(dāng)我們不想對外公開某個(gè)方法時(shí),可以使用修飾符private實(shí)現(xiàn)颁虐,例如

    var name: String = ""
     private set
    

    但是這種方式只適用于set方法蛮原,get的訪問權(quán)限默認(rèn)是和屬性一致的,下面的使用會報(bào)編譯錯(cuò)誤

    var name: String = ""
     private get             // 編譯錯(cuò)誤另绩,get的訪問權(quán)限和屬性一致
    

    如果你們有一個(gè)需求儒陨,要求對某個(gè)屬性只可寫,不可讀笋籽,那么請用方法fun實(shí)現(xiàn)吧……

  3. 自定義getter和setter

    上面說了getset的默認(rèn)實(shí)現(xiàn)蹦漠,那么就再來說說自定義實(shí)現(xiàn),當(dāng)然车海,像上面說過的笛园,varsetgetval只有get

    // 自定義get
    var size: Int = 2
        get() = if (field > 10) 15 else 0
    
    // 調(diào)用
    var pf = PropertiesFields()
    pf.size = 5
    Log.d("text", "size : ${pf.size}")
    pf.size = 20
    Log.d("text", "size : ${pf.size}")
    
    // 輸出
    size : 0
    size : 15
    
    // 自定義set
    var size: Int = 2
        set(value) {
            field = if (value > 10) 15 else 0
        }
    
    // 調(diào)用和輸出同上
    

    上面的代碼很簡單侍芝,就是一個(gè)if表達(dá)式 喘沿,就不多解釋了。其實(shí)kotlin中的gettersetter理解起來很簡單竭贩,就像java中的所有屬性都是private并且實(shí)現(xiàn)了gettersetter蚜印,其他的像權(quán)限和自定義問題,都和java中類似留量。

三窄赋、后端變量(Backing Fields)

看大神是這么翻譯Backint Fields的,那我們也這么叫好了楼熄。這里就要說到上面提到的field了忆绰,在kotlin的gettersetter是不允許本身的局部變量的,因?yàn)閷傩缘恼{(diào)用也是對get的調(diào)用可岂,因此會產(chǎn)生遞歸错敢,造成內(nèi)存溢出。

var count = 1
var size: Int = 2
set(value) {
    Log.e("text", "count : ${count++}")
    size = if (value > 10) 15 else 0
}
內(nèi)存溢出.png

kotlin為此提供了一種我們要說的后端變量,也就是field稚茅。編譯器會檢查函數(shù)體纸淮,如果使用到了它,就會生成一個(gè)后端變量亚享,否則就不會生成咽块。我們在使用的時(shí)候,用field代替屬性本身進(jìn)行操作欺税。

var size: Int = 2
val isEmpty: Boolean
    get() = this.size == 0

這是官方文檔的一個(gè)例子侈沪,在訪問屬性值isEmpty時(shí),并不會生成后端變量晚凿。

這里我有一個(gè)疑惑亭罪。我們說過類屬性是一定要初始化的,但是我在編譯這個(gè)例子的時(shí)候確實(shí)是沒問題的歼秽。我又嘗試著將isEmpty改為普通變量皆撩,然后就編譯出錯(cuò),希望朋友能幫我解惑哲银,謝謝扛吞。

var size: Int = 2
var isEmpty: Boolean          // 這樣就編譯報(bào)錯(cuò)
    get() = this.size == 0

四、后端屬性

如果上面的方案都不符合你的需求荆责,那么可以試試“后端屬性”(backing property)的方法滥比,它實(shí)際上也是隱含試的對屬性值的初始化聲明,避免了空指針做院。

private var _table: Map<String, Int>? = null
val table: Map<String, Int>
    get() {
        if (_table == null)
            _table = HashMap() // Type parameters are inferred
        return _table ?: throw AssertionError("Set to null by another thread")
    }

從各種角度看盲泛,這和在Java中定義Bean屬性的方式一樣。因?yàn)樵L問私有的屬性的gettersetter函數(shù)键耕,會被編譯器優(yōu)化成直接反問其實(shí)際字段寺滚。

五、編譯器常數(shù)值

如果在編譯期間屈雄,屬性值就能被確定村视,該類屬性值使用const 修飾符,將屬性標(biāo)記為編譯期常數(shù)值(compile timeconstants). 這類屬性必須滿足以下所有條件:

  • 必須是頂級屬性酒奶,或者是一個(gè)object的成員
  • 值被初始化為 String 類型蚁孔,或基本類型(primitive type)
  • 不存在自定義的取值方法

六、延遲初始化屬性(lateinit)

我們說過惋嚎,在類內(nèi)聲明的屬性必須初始化杠氢,如果設(shè)置非NULL的屬性,應(yīng)該將此屬性在構(gòu)造器內(nèi)進(jìn)行初始化另伍。假如想在類內(nèi)聲明一個(gè)NULL屬性鼻百,在需要時(shí)再進(jìn)行初始化(最典型的就是懶漢式單例模式),與Kotlin的規(guī)則是相背的,此時(shí)我們可以聲明一個(gè)屬性并延遲其初始化温艇,此屬性用lateinit修飾符修飾因悲。

// 延遲初始化聲明
lateinit var late : String
fun initLate(){
    late = "I am late"
}

// 先調(diào)用方法,再調(diào)用屬性
var pf = PropertiesFields()
pf.initLate()
Log.d("text", pf.late)

// 輸出
I am late

需要注意的是中贝,我們在使用的時(shí)候,一定要確保屬性是被初始化過的臼朗,通常先調(diào)用初始化方法邻寿,否則會有異常。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末视哑,一起剝皮案震驚了整個(gè)濱河市绣否,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌挡毅,老刑警劉巖蒜撮,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異跪呈,居然都是意外死亡段磨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進(jìn)店門耗绿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來苹支,“玉大人,你說我怎么就攤上這事误阻≌郏” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵究反,是天一觀的道長寻定。 經(jīng)常有香客問我,道長精耐,這世上最難降的妖魔是什么狼速? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮卦停,結(jié)果婚禮上唐含,老公的妹妹穿的比我還像新娘。我一直安慰自己沫浆,他們只是感情好捷枯,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著专执,像睡著了一般淮捆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天攀痊,我揣著相機(jī)與錄音桐腌,去河邊找鬼。 笑死苟径,一個(gè)胖子當(dāng)著我的面吹牛案站,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播棘街,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼蟆盐,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了遭殉?” 一聲冷哼從身側(cè)響起石挂,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎险污,沒想到半個(gè)月后痹愚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蛔糯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年拯腮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚁飒。...
    茶點(diǎn)故事閱讀 40,615評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡疾瓮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出飒箭,到底是詐尸還是另有隱情狼电,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布弦蹂,位于F島的核電站肩碟,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏凸椿。R本人自食惡果不足惜削祈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望脑漫。 院中可真熱鬧髓抑,春花似錦、人聲如沸优幸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽网杆。三九已至羹饰,卻和暖如春伊滋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背队秩。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工笑旺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人馍资。 一個(gè)月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓筒主,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鸟蟹。 傳聞我的和親對象是個(gè)殘疾皇子乌妙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容