Kotlin學(xué)習(xí)筆記之 5 類和對象

首發(fā)于公眾號: DSGtalk1989

5.Kotlin 類和對象

  • 構(gòu)造器

    kotlin中一個類只能有一個主構(gòu)造器和一個或多個次構(gòu)造器西轩。主構(gòu)造器可以直接跟在class定義的類名后面但是沒有方法體,如下:

    class Person constructor(s : String) {
    }
    //也可以寫成這樣考余,記得,沒有空格
    class Person(s : String){
    }
    //一旦構(gòu)造函數(shù)存在修飾符或者是注解的情況下靠益,我們就不能省去constructor的方法名
    class Child public constructor(s : String){
    }
    

    以上的都是主構(gòu)造函數(shù)液肌,次構(gòu)造函數(shù)定義在類中,并且kotlin強制規(guī)定邻耕,次構(gòu)造函數(shù)必須要調(diào)用主構(gòu)造函數(shù),如下:

    class Person constructor(s : String) {
          constructor(i : Int) : this("123"){
          }
    }
    
    //也可以沒有主構(gòu)造器燕鸽,如下的方式就是直接起的次構(gòu)造器
    //只有這種情況下兄世,次構(gòu)造器不需要再調(diào)用主構(gòu)造器,所以一般如下這種方式跟我們的java習(xí)慣比較像
    class Person{
         constructor(){
         }
    }
    
  • private啊研,public御滩,protected,internal

    前面三個大家比較熟悉了党远,在java中都有削解,但是internal是kotlin中才引入的,叫做模塊內(nèi)可見沟娱,即同一個module中可見氛驮。

    我們分別來看下,用這四個修飾符來描述屬性所帶來的編譯區(qū)別济似。

    //kt
    private var a = "a"
    public var b = "b"
    protected var c = "c"
    internal var d = "d"
    
    
    //decompiled
    private String a;
     @NotNull
     private String b;
     @NotNull
     private String c;
     @NotNull
     private String d;
    
     @NotNull
     public final String getB() {
        return this.b;
     }
    
     public final void setB(@NotNull String var1) {
        Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
        this.b = var1;
     }
    
     @NotNull
     protected final String getC() {
        return this.c;
     }
    
     protected final void setC(@NotNull String var1) {
        Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
        this.c = var1;
     }
    
     @NotNull
     public final String getD$app_debug() {
        return this.d;
     }
    
     public final void setD$app_debug(@NotNull String var1) {
        Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
        this.d = var1;
     }
    
    

    總結(jié)一下矫废,就是不管是哪一個修飾符,最終經(jīng)過編譯之后生成的在java中的參數(shù)描述都是private的砰蠢,修飾符真正造成的區(qū)別是在編譯了自后的getset的方法不同蓖扑。

    private的話,就不會生成getset方法台舱,因為對于這個參數(shù)來說律杠,是外部不可訪問的。publicprotected就是相應(yīng)的setget竞惋。而internal則是publicsetD$app_debuggetD$app_debug方法俩功。我們可以認(rèn)為這兩個方法,在model中都是可以被訪問的碰声。

  • init關(guān)鍵字

    上面說到主構(gòu)造器直接寫在類名之后是沒有方法體的,因此一旦我們想要在構(gòu)造函數(shù)中做一些初始化的操作熬甫,就需要挪到init中實現(xiàn)了胰挑。

    class Person constructor(firstName: String) {
          init {
              println("FirstName is $firstName")
          }
    }
    
  • getter和setter

    跟java差的有點多,首先屬性定義前面說過了,kotlin中g(shù)etter和setter直接定義在屬性下方瞻颂,由于kotlin的本身屬性的直接訪問性豺谈,只要你創(chuàng)建的是public的屬性,都可以直接獲取到屬性值即get方法和修改屬性值即set方法贡这。

    所以免去了為了fastjson而專門去寫的settergetter茬末,但是一旦你需要在gettersetter時做一些其他的操作,我們就需要去顯示的寫出getset

    var sex = "boy"
          get() {
              return "girl"
          }
          set(value) {
              when {
                  value.contains("girl") -> field = "boy"
              }
          }
    
    var age = 16
          get() = field + 10
          private set(value) = action(value)
          
    fun action(int: Int) {
    
    }      
          
    

    getset可以直接包含方法體盖矫,也可以直接通過等號的方式鏈到單行表達(dá)式或者方法丽惭。
    即我們認(rèn)為,一旦觸發(fā)取值和賦值的時候會做相應(yīng)的操作辈双。其中field就是指的他自己责掏。

    >>注:field的重要性<<

    在kotlin中,我們定義了一個參數(shù)name然后湃望,只要去調(diào)用他换衬,比如parent.name或者說去對他進(jìn)行賦值name = "Tony"最終都會被編譯成使用了getset方法,如下

     //Observer.kt
     fun main(args : Array<String>){
          var observer = Observer()
          observer.name = "Tony"
          var newName = observer.name
      }
      
      //Observer.decompiled.java
      public static final void main(@NotNull String[] args) {
        Intrinsics.checkParameterIsNotNull(args, "args");
        Observer observer = new Observer();
        //調(diào)用了set
        observer.setName("Tony");
        //調(diào)用了get
        String newName = observer.getName();
     }
    

    所以我們在類中定義屬性的settergetter的時候证芭,如果直接操作屬性本身瞳浦,就會出現(xiàn)死循環(huán)。這就是field的用途

    //kt
    var no: Int = 100
      get() = no
      set(value) {
          if (value < 10) {       // 如果傳入的值小于 10 返回該值
              no = value
          } else {
              no = -1         // 如果傳入的值大于等于 10 返回 -1
          }
    }
    
    //decompiled
    int no = 100;
      public int getNo() {
          return getNo();// Kotlin中的get() = no語句中出來了變量no废士,直接被編譯器理解成“調(diào)用getter方法”
      }
      
      public void setNo(int value) {
          if (value < 10) {
              setNo(value);// Kotlin中出現(xiàn)“no =”這樣的字樣叫潦,直接被編譯器理解成“這里要調(diào)用setter方法”
          } else {
              setNo(-1);// 在setter方法中調(diào)用setter方法,這是不正確的
          }
      }
    

    很顯然湃密,造成了死循環(huán)诅挑。

  • lateinit關(guān)鍵字

    我們都知道kotlin中,在方法中定義屬性時泛源,我們必須進(jìn)行初始化的操作拔妥。而類中本身我們一開始并不知道他到底是什么,所以會有希望晚一點再初始化的需求达箍,這里就可以使用lateinit關(guān)鍵字來描述没龙,那我們就可以不用給出具體的初始化值,但是kotlin會要求你必須給出屬性的類型缎玫。

    lateinit var game : String
    

    那么這個game我們可以之后再對其賦值硬纤。

    從kotlin 1.2開始已經(jīng)支持全局和局部變量都是用lateinit, 并且我們可以通過isInitialized來判斷是否已經(jīng)初始化過

  • 抽象類

    我們默認(rèn)定義的class都是final的赃磨,無法被繼承的筝家。所以一旦需要這個class能夠被繼承,我們需要加上open關(guān)鍵字邻辉。如果這是個抽象類溪王,那我們需要添加abstract關(guān)鍵字腮鞍。一旦被abstract描述,就無需再加上open了莹菱。

    open class Person(){
    }
    
    abstract class Parent{
    }
    

    緊接著移国,另外幾種場景

    • 方法是否可以被重寫

      默認(rèn)方法都是final的,如果需要讓方法可以被重寫道伟,需要在方法前再加上open

      所有我們平時在java中寫的一個單純的類實際上轉(zhuǎn)換成kotlin是如下這個樣子的:

      open class Person constructor(s: String) {
              open fun getName(){}
      }
      

      同樣的abstract也是這個意思迹缀,加載class前面只是形容類,跟方法和屬性什么的一點關(guān)系都沒有

    • 抽象屬性

      這是一個比較新的東西蜜徽,因為java不支持抽象屬性祝懂。就是說,你要是繼承我娜汁,你就必須要初始化我所要求初始化的屬性嫂易。

      abstract class Parent(ame : String){
             abstract var ame : String
      }
      
      //兩種集成方式,一種是直接在構(gòu)造函數(shù)中對抽象屬性進(jìn)行復(fù)寫
      //由于父類構(gòu)造需要傳一個字符串掐禁,所以在繼承時也需要直接傳入怜械,此處傳入的是Child1自己的構(gòu)造參數(shù)s
      class Child1 constructor(s: String, override var ame: String) : Parent(s) {
      }
      //一種是在類中對屬性進(jìn)行復(fù)寫
      class Child2  constructor(s: String) : Parent(s) {
             override lateinit var ame: String
       }
       
       //如果子類沒有主構(gòu)造函數(shù),也可以通過次構(gòu)造函數(shù)調(diào)用`super`方法實現(xiàn)
       class Child: Parent {
             constructor() : super("s")
             override lateinit var ame: String
         }
      
  • 嵌套類

    直接在class內(nèi)部定義class傅事,基本和java差不多

    class Outer {                  // 外部類
        private val bar: Int = 1
        class Nested {             // 嵌套類
            fun foo() = 2
        }
    }
    
    fun main(args: Array<String>) {
        val demo = Outer.Nested().foo() // 調(diào)用格式:外部類.嵌套類.嵌套類方法/屬性
        println(demo)    // == 2
    }
    
  • 內(nèi)部類

    在剛才嵌套類的基礎(chǔ)上加上inner的關(guān)鍵字申明缕允。

    class Outer {
         private val bar: Int = 1
         var v = "成員屬性"
         /**嵌套內(nèi)部類**/
         inner class Inner {
             fun foo() = bar  // 訪問外部類成員
             fun innerTest() {
                 var o = this@Outer //獲取外部類的成員變量
                 println("內(nèi)部類可以引用外部類的成員,例如:" + o.v)
             }
         }
     }
    

    唯一的區(qū)別在于內(nèi)部類持有了外部類的引用蹭越,可以通過@外部類名的方式障本,來訪問外部類的成員變量。

  • 內(nèi)部類和嵌套類的區(qū)別

    我們來看如下兩個嵌套類和內(nèi)部類的以及讓門各自編譯成class文件的例子

    //Out.kt
    class Out{
         class Inner{
     
         }
     }
     
     //Out.decompiled.java
     public final class Out {
        public static final class Inner {
        }
     }
     
     //Out.kt
    class Out{
         inner class Inner{
     
         }
     }
     
     
     //Out.decompiled.java
     public final class Out {
        public final class Inner {
        }
     }
    

    已經(jīng)很明顯了响鹃,inner之所以持有外部的引用驾霜,是因為他不是static的。也就是說kotlin的class默認(rèn)就是static final的买置。

    調(diào)用嵌套類的方式與調(diào)用內(nèi)部類的方式差別也只是一個括號而已

    fun main(args : Array<String>){
        //內(nèi)部類調(diào)用
         Out().Inner().method()
         //嵌套類的調(diào)用
         Out1.Inner().method()
     }
    

    其實比較容易理解粪糙,嵌套類是static的直接可以通過類名來進(jìn)行訪問嵌套類。

  • 匿名內(nèi)部類

    一般用在接口層面的很多忿项,我們通常知道的是傳參是個接口蓉冈,方法中調(diào)用了接口方法的形式,如下:

    class Observer{
         fun getIt(listener: Listener){
             listener.onClick()
         }
     }
     
     interface Listener{
         fun onClick()
     }
     
     fun main(args : Array<String>){
         var observer = Observer()
         //注意轩触,此處的object是kotlin獨有的關(guān)鍵字
         //不是隨便寫寫的寞酿,匿名內(nèi)部類必須通過這個關(guān)鍵字來申明
         observer.getIt(object : Listener{
             override fun onClick() {
                 TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
             }
         })
     }
    

    通常我們也可以直接使用接口名 + lambda表達(dá)式的方式來生成匿名內(nèi)部類,但條件是這個接口必須是函數(shù)式j(luò)ava接口脱柱,即只有一個抽象方法的java文件中定義的接口伐弹。

    比如我們基本碰到的所有的什么OnclickListener等等

    tv_case_id.setOnClickListener { View.OnClickListener{
    
     } }
    

    不過kotlin中定義的接口,我們就必須通過object的方式去實現(xiàn)了

    同時匿名內(nèi)部類我們可以單獨的拿出來進(jìn)行定義榨为,實際上我們可以把object :理解成一個匿名的內(nèi)部類實現(xiàn)了一個接口惨好,也就是說我們還可以實現(xiàn)多個接口椅邓,比如:

    open class A(x: Int) {
         public open val y: Int = x
     }
     
     interface B { …… }
     
     val ab: A = object : A(1), B {
         override val y = 15
     }
    

    通常我們在java中是無法做到匿名內(nèi)部類實現(xiàn)多個接口的,因為我們只能new一個接口出來昧狮。

更甚者說,我們很多時候甚至不需要這個object去實現(xiàn)或者是繼承什么板壮,我們可以直接搞一個object出來

fun foo() {
     val adHoc = object {
         var x: Int = 0
         var y: Int = 0
     }
     print(adHoc.x + adHoc.y)
 }
  • 匿名對象最為函數(shù)的返回類型

    我們上面是將匿名對象賦值給了對象逗鸣,我們還可以吧匿名對象直接賦值給方法,比如下面這個樣子绰精。

    fun publicFoo() = object {
        val x: String = "x"
    }
    

    這里涉及到公有還是私有的問題撒璧。

    匿名對象我們一般只能用在私有域和本地。白話的說就是一旦變成了公有笨使,那就說誰都可以去調(diào)用卿樱,由于匿名對象只在生命的本地和私有域起作用,導(dǎo)致公有調(diào)用拿到的對象只能是匿名對象的超類(即父類硫椰,比如上面的object : Listener就是Listener繁调,如果沒有顯式的定義超類就是Any)那么這樣一來,就會導(dǎo)致匿名內(nèi)部類中定義的屬性是拿不到的靶草,比如上面的x蹄胰,因為上面的object并沒有顯式的定義超類,所以他返回的是Any奕翔,而Any是沒有x屬性的.

  • 匿名對象訪問變量

    在java中匿名內(nèi)部類想要訪問相應(yīng)的屬性變量必須要final才行裕寨,但是在kotlin中,我們直接可以訪問包含匿名對象作用域中的所有變量派继。

    fun countClicks(window: JComponent) {
        var clickCount = 0
        var enterCount = 0
    
        window.addMouseListener(object : MouseAdapter() {
            override fun mouseClicked(e: MouseEvent) {
                clickCount++
            }
    
            override fun mouseEntered(e: MouseEvent) {
                enterCount++
            }
        })
    }
    

Kotlin學(xué)習(xí)筆記之 1 基礎(chǔ)語法

Kotlin學(xué)習(xí)筆記之 2 基本數(shù)據(jù)類型

Kotlin學(xué)習(xí)筆記之 3 條件控制

Kotlin學(xué)習(xí)筆記之 4 循環(huán)控制

Kotlin學(xué)習(xí)筆記之 5 類和對象

Kotlin學(xué)習(xí)筆記之 6 繼承

Kotlin學(xué)習(xí)筆記之 7 接口

Kotlin學(xué)習(xí)筆記之 8 擴展

Kotlin學(xué)習(xí)筆記之 9 數(shù)據(jù)類與密封類

Kotlin學(xué)習(xí)筆記之 10 泛型

Kotlin學(xué)習(xí)筆記之 11 枚舉類

Kotlin學(xué)習(xí)筆記之 12 對象表達(dá)式和對象聲明

Kotlin學(xué)習(xí)筆記之 13 基礎(chǔ)操作符run宾袜、with、let驾窟、also庆猫、apply

Kotlin學(xué)習(xí)筆記之 14 包與導(dǎo)入

Kotlin學(xué)習(xí)筆記之 15 伴生對象

Kotlin學(xué)習(xí)筆記之 16 委托

Kotlin學(xué)習(xí)筆記之 17 可觀察屬性

Kotlin學(xué)習(xí)筆記之 18 函數(shù)

Kotlin學(xué)習(xí)筆記之 19 高階函數(shù)與 lambda 表達(dá)式

Kotlin學(xué)習(xí)筆記之 20 內(nèi)聯(lián)函數(shù)

Kotlin學(xué)習(xí)筆記之 21 解構(gòu)聲明

Kotlin學(xué)習(xí)筆記之 22 集合

Kotlin學(xué)習(xí)筆記之 23 相等判斷

Kotlin學(xué)習(xí)筆記之 24 操作符重載

Kotlin學(xué)習(xí)筆記之 25 異常捕捉

Kotlin學(xué)習(xí)筆記之 26 反射

Kotlin學(xué)習(xí)筆記之 27 類型別名

Kotlin學(xué)習(xí)筆記之 28 協(xié)程基礎(chǔ)

Kotlin學(xué)習(xí)筆記之 29 上下文與調(diào)度器

Kotlin學(xué)習(xí)筆記之 30 協(xié)程取消與超時

Kotlin學(xué)習(xí)筆記之 31 協(xié)程掛起函數(shù)的組合

Kotlin學(xué)習(xí)筆記之 32 協(xié)程異常處理

Kotlin學(xué)習(xí)筆記之 33 協(xié)程 & Retrofit

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市纫普,隨后出現(xiàn)的幾起案子阅悍,更是在濱河造成了極大的恐慌,老刑警劉巖昨稼,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件节视,死亡現(xiàn)場離奇詭異,居然都是意外死亡假栓,警方通過查閱死者的電腦和手機寻行,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來匾荆,“玉大人拌蜘,你說我怎么就攤上這事杆烁。” “怎么了简卧?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵兔魂,是天一觀的道長。 經(jīng)常有香客問我举娩,道長析校,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任铜涉,我火速辦了婚禮智玻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘芙代。我一直安慰自己吊奢,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布纹烹。 她就那樣靜靜地躺著页滚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪滔韵。 梳的紋絲不亂的頭發(fā)上逻谦,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天,我揣著相機與錄音陪蜻,去河邊找鬼邦马。 笑死,一個胖子當(dāng)著我的面吹牛宴卖,可吹牛的內(nèi)容都是我干的滋将。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼症昏,長吁一口氣:“原來是場噩夢啊……” “哼随闽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起肝谭,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤掘宪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后攘烛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體魏滚,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年坟漱,在試婚紗的時候發(fā)現(xiàn)自己被綠了鼠次。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖腥寇,靈堂內(nèi)的尸體忽然破棺而出成翩,到底是詐尸還是另有隱情,我是刑警寧澤赦役,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布麻敌,位于F島的核電站,受9級特大地震影響掂摔,放射性物質(zhì)發(fā)生泄漏庸论。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一棒呛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧域携,春花似錦簇秒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锋边,卻和暖如春皱坛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背豆巨。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工剩辟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人往扔。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓贩猎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親萍膛。 傳聞我的和親對象是個殘疾皇子吭服,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,440評論 2 359