Kotlin學(xué)習(xí)筆記:類和接口

Kotlin學(xué)習(xí)筆記:概述
Kotlin學(xué)習(xí)筆記:基本語法和函數(shù)
Kotlin學(xué)習(xí)筆記:類和接口
Kotlin學(xué)習(xí)筆記:lambda編程
Kotlin學(xué)習(xí)筆記:類型系統(tǒng)
Kotlin學(xué)習(xí)筆記:泛型
Kotlin學(xué)習(xí)筆記:注解和反射

類和接口思維導(dǎo)圖

在Kotlin中,類默認(rèn)是final和public的。如果該類需要被繼承,則可以用open關(guān)鍵字顯示的聲明類。

可見性修飾符

image.png

構(gòu)造方法

Kotlin引入了constructor和init關(guān)鍵字。constructor關(guān)鍵字用于聲明一個(gè)主構(gòu)造方法或者從構(gòu)造方法。而init是一個(gè)初始化語句塊。這個(gè)語句塊會(huì)在類創(chuàng)建時(shí),與主構(gòu)造方法一起使用。因?yàn)橹鳂?gòu)造方法不能包含初始化代碼。

class User constructor(_name: String){
    val name: String
    init {
        this.name = _name
    }
}

從構(gòu)造方法

雖然大多數(shù)在Java中需要重載的構(gòu)造方法場景都可以使用參數(shù)默認(rèn)值和參數(shù)命名的方法解決掉,但是,還是有些場景需要聲明多個(gè)構(gòu)造方法,以便于以不同的方式初始化類。

例如,在Android中自定義View,往往就需要聲明多個(gè)構(gòu)造方法。此時(shí),可以使用從構(gòu)造方法。從構(gòu)造方法可以聲明任意多個(gè)。

class CustomView: View {
    constructor(context: Context) : super(context) {
        //...
    }
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        // ...
    }
}

內(nèi)部類

與java不同的是,默認(rèn)情況下,內(nèi)部類不能訪問外部類。如果內(nèi)部類想要訪問外部類,需要使用inline關(guān)鍵字顯示聲明內(nèi)部類。

image.png

sealed 類(密封類)

如果父類使用sealed修飾符,那么它會(huì)對(duì)可能創(chuàng)建的子類做出嚴(yán)格限制,并且所有子類必須嵌套在父類中。

數(shù)據(jù)類

在Java中,會(huì)出現(xiàn)很多樣板代碼,比如setter/getter,toString,equals,hashCode等方法。為了消除這些樣板代碼,Kotlin引用了數(shù)據(jù)類,即使用data關(guān)鍵字修飾類即可。

下面我們創(chuàng)建了一個(gè)User數(shù)據(jù)類,它有兩個(gè)屬性,姓名和年齡。

data class Person(val name:String, val age: Int)

在使用時(shí),我們可以直接user1.name或user1.toString()調(diào)用。

為了更清楚的看到數(shù)據(jù)類是如何工作的,我們可以查看這個(gè)類的字節(jié)碼。將字節(jié)碼反編譯為java代碼,如下所示。

@Metadata(
   mv = {1, 1, 9},
   bv = {1, 0, 2},
   k = 1,
   d1 = {"\u0000 \n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\b\n\u0002\b\t\n\u0002\u0010\u000b\n\u0002\b\u0004\b\u0086\b\u0018\u00002\u00020\u0001B\u0015\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u0012\u0006\u0010\u0004\u001a\u00020\u0005¢\u0006\u0002\u0010\u0006J\t\u0010\u000b\u001a\u00020\u0003H?\u0003J\t\u0010\f\u001a\u00020\u0005H?\u0003J\u001d\u0010\r\u001a\u00020\u00002\b\b\u0002\u0010\u0002\u001a\u00020\u00032\b\b\u0002\u0010\u0004\u001a\u00020\u0005H?\u0001J\u0013\u0010\u000e\u001a\u00020\u000f2\b\u0010\u0010\u001a\u0004\u0018\u00010\u0001H?\u0003J\t\u0010\u0011\u001a\u00020\u0005H?\u0001J\t\u0010\u0012\u001a\u00020\u0003H?\u0001R\u0011\u0010\u0004\u001a\u00020\u0005¢\u0006\b\n\u0000\u001a\u0004\b\u0007\u0010\bR\u0011\u0010\u0002\u001a\u00020\u0003¢\u0006\b\n\u0000\u001a\u0004\b\t\u0010\n¨\u0006\u0013"},
   d2 = {"Lcom/nxiangbo/kotlin_learning/User;", "", "name", "", "age", "", "(Ljava/lang/String;I)V", "getAge", "()I", "getName", "()Ljava/lang/String;", "component1", "component2", "copy", "equals", "", "other", "hashCode", "toString", "production sources for module app"}
)
public final class User {
   @NotNull
   private final String name;
   private final int age;

   @NotNull
   public final String getName() {
      return this.name;
   }

   public final int getAge() {
      return this.age;
   }

   public User(@NotNull String name, int age) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      super();
      this.name = name;
      this.age = age;
   }

   @NotNull
   public final String component1() {
      return this.name;
   }

   public final int component2() {
      return this.age;
   }

   @NotNull
   public final User copy(@NotNull String name, int age) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      return new User(name, age);
   }

   // $FF: synthetic method
   // $FF: bridge method
   @NotNull
   public static User copy$default(User var0, String var1, int var2, int var3, Object var4) {
      if ((var3 & 1) != 0) {
         var1 = var0.name;
      }

      if ((var3 & 2) != 0) {
         var2 = var0.age;
      }

      return var0.copy(var1, var2);
   }

   public String toString() {
      return "User(name=" + this.name + ", age=" + this.age + ")";
   }

   public int hashCode() {
      return (this.name != null ? this.name.hashCode() : 0) * 31 + this.age;
   }

   public boolean equals(Object var1) {
      if (this != var1) {
         if (var1 instanceof User) {
            User var2 = (User)var1;
            if (Intrinsics.areEqual(this.name, var2.name) && this.age == var2.age) {
               return true;
            }
         }

         return false;
      } else {
         return true;
      }
   }
}

由此可見,在編譯器將代碼編譯為字節(jié)碼時(shí),為User類自動(dòng)添加了上述這些方法。

除了常用的toString等方法外,還自動(dòng)生成了一個(gè)copy方法。那么,這個(gè)copy方法的作用是什么呢?為了讓使用不可變對(duì)象的數(shù)據(jù)類變得更容易,Kotlin提供了一個(gè)copy方法,以便于通過創(chuàng)建副本的方式修改數(shù)據(jù)類

object關(guān)鍵字

object關(guān)鍵字有三種應(yīng)用場景:

  • 單例
  • 伴生對(duì)象(companion object)
  • object表達(dá)式,可以替代Java匿名內(nèi)部類

單例

object聲明將 類聲明和單一實(shí)例聲明結(jié)合在一起。下面創(chuàng)建一個(gè)object

object ObjectDemo{  
}

反編譯為Java代碼為

public final class ObjectDemo {
   public static final ObjectDemo INSTANCE;

   static {
      ObjectDemo var0 = new ObjectDemo();
      INSTANCE = var0;
   }
}

companion object

可以包含工廠方法或者與該類相關(guān)但不需要該類實(shí)例的方法。因?yàn)轫攲雍瘮?shù)不能訪問類的私有方法,而companion object 可以訪問類的私有屬性和方法。

abstract class RepoDatabase : RoomDatabase() {
    abstract fun repos(): RepoDao

    companion object {

        @Volatile
        private var INSTANCE: RepoDatabase? = null

        fun getInstance(context: Context): RepoDatabase = INSTANCE ?: synchronized(this) {
            INSTANCE ?: buildDatabase(context).also { INSTANCE = it }
        }

        private fun buildDatabase(context: Context) =
                Room.databaseBuilder(context, RepoDatabase::class.java, "zhihu.db").build()
    }
}

object表達(dá)式

可以替代匿名內(nèi)部類

        val vto = layout.viewTreeObserver
        vto.addOnGlobalLayoutListener(object: ViewTreeObserver.OnGlobalLayoutListener       {
            @RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
            override fun onGlobalLayout(){
                layout.viewTreeObserver.removeOnGlobalLayoutListener(this)
                val height = layout.measuredHeight
                val width = layout.measuredWidth
            }
        })

接口

接口與Java接口聲明一致,不過Kotlin接口方法可以有默認(rèn)實(shí)現(xiàn)。

interface Clickable {
    fun click() // 正常方法
    fun showOff() = println("I'm clickable!") // 方法的默認(rèn)實(shí)現(xiàn)
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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