Kotlin基礎(chǔ)(二)類、對象和接口

修飾符

訪問修飾符
修飾符 相關(guān)成員 評注
final 不能被重寫 類中成員默認(rèn)使用
open 可被重寫 需要明確什么
abstract 必須被重寫 在抽象類中使用
override 重寫父類或接口中的成員 若沒有final聲明止吁,重寫的成員默認(rèn)是open

Java中可以創(chuàng)建任意類的子類并重寫任意方法什乙,除非顯示聲明final挽封。對基類的修改會導(dǎo)致子類不正確的行為,即脆弱的基類問題。Effective Java建議“要么為繼承做好設(shè)計(jì)并記錄文檔辅愿,要么禁止智亮。”Kotlin采用該思想哲學(xué)点待,Java中類和方法默認(rèn)是open的阔蛉,而Kotlin中類和方法默認(rèn)是final

open class RichButtion:Clickable{
    //默認(rèn)是final不能被重寫
    fun disable(){}
    //open可重寫
    open fun animate(){}
    //override 方法默認(rèn)是open
    override fun click(){}
}

接口和抽象類默認(rèn)是open, 其抽象成員默認(rèn)是open

abstract class Animate{
    //默認(rèn)是open
    abstract fun animate()
    //非抽象方法默認(rèn)是final
    fun animateTwice(){}
}
可見性修飾符
修飾符 類成員 頂層聲明
public(默認(rèn)) 所有地方可見 所有地方可見
internal 模塊中可見 模塊中可見
protected 子類中可見 ——
private 類中可見 文件中可見

Java中默認(rèn)可見性——包私有癞埠,在kotlin中并沒有状原。Kotlin只把包作為命名空間里組織代碼的一種方式,并沒有將其用作可見性控制苗踪。作為替代方案颠区,Koltin是使用新的修飾符internal,表示“只能在模塊內(nèi)可見⊥ú”internal優(yōu)勢在于它對模塊實(shí)現(xiàn)細(xì)節(jié)提供真正的封裝毕莱。

接口

接口包含抽象方法的定義和非抽象方法的實(shí)現(xiàn),但是他們都不能包含任何狀態(tài)颅夺。

interface Clickable {
    //不支持backing-field朋截,不能存儲值
    var clickable: Boolean
    //默認(rèn)open,可被重寫
    fun click()
    //默認(rèn)final吧黄,不能被重寫
    fun showOff() = println("I'm Clickable")
}

由于Koltin 1.0Java 6為目標(biāo)設(shè)計(jì)部服,其并不支持接口中的默認(rèn)方法,因此會把每個默認(rèn)方法的接口編譯成一個普通接口和一個將方法體作為靜態(tài)函數(shù)的類的結(jié)合體拗慨,如上面的接口反編譯后看到:

public interface Clickable {
   boolean getClickable();
   void setClickable(boolean var1);
   void click();
   void showOff();
 
   public static final class DefaultImpls {
      public static void showOff(Clickable $this) {
         String var1 = "I'm Clickable";
         boolean var2 = false;
         System.out.println(var1);
      }
   }
}

構(gòu)造函數(shù)

Kotlin構(gòu)造函數(shù)相對于Java做了部分修改廓八,區(qū)分主構(gòu)造函數(shù)從構(gòu)造函數(shù)初始化塊中的代碼實(shí)際上會成為主構(gòu)造函數(shù)的?部分胆描。委托給主構(gòu)造函數(shù)會作為次構(gòu)造函數(shù)的第?條語句瘫想,因此所有初始化塊中的代碼都會在次構(gòu)造函數(shù)體之前執(zhí)?。

class Person {
    init {
        println("Init block")
    }
    constructor(i: Int) {
        println("Constructor")
    }
}

在大多數(shù)場景中昌讲,類的構(gòu)造函數(shù)非常簡明:要么沒有參數(shù)国夜,要么直接把參數(shù)于對應(yīng)的屬性關(guān)聯(lián)

class User(val nickname:String,val isSubscribed:Boolean=false)

如果類有主構(gòu)造函數(shù),每個從構(gòu)造函數(shù)需要委托主構(gòu)造函數(shù)短绸,可直接委托或者間接委托车吹。

class User(val nickname: String) {
    var isSubscribed: Boolean?=null
    constructor(_nickname: String, _isSubscribed: Boolean) : this(_nickname) {
        this.isSubscribed = _isSubscribed
    }
}

如何該類有父類,應(yīng)該顯式的調(diào)用父類的構(gòu)造方法

//Clickable為接口醋闭,沒有構(gòu)造函數(shù)
class Buttion:Clickable{

}
//即便沒有任何參數(shù)窄驹,也要顯示調(diào)用父類構(gòu)造函數(shù)
class RiseButton:Button(){
    
}
//如果有多級構(gòu)造函數(shù),可以super關(guān)鍵字調(diào)用父類構(gòu)造
class MyButton: View {
    constructor(ctx:Context):super(ctx)
    constructor(ctx: Context,attributes: AttributeSet?):super(ctx,attributes)
}
內(nèi)部類证逻、嵌套類乐埠、密封類、數(shù)據(jù)類·
內(nèi)部類和嵌套類

Kotlin中嵌套類不能訪問外部類的實(shí)例,類似Java靜態(tài)內(nèi)部類丈咐;而Kotlin中的內(nèi)部類需要用inner關(guān)鍵字修飾才能訪問外部類的實(shí)例瑞眼。

class Outer {
    private val bar: Int = 1
    //內(nèi)部類
    inner class Inner {
        fun foo() = bar
    }
}
class Outer2 {
    private val bar: Int = 1
    //嵌套類,不持有外部類的引用
    class Nested {
        fun foo() = 2
    }
}
val demo = Outer().Inner().foo() // == 1
val demo2 = Outer2.Nested().foo() // == 2
密封類

密封類?來表?受限的類繼承結(jié)構(gòu):當(dāng)?個值為有限集中的類型棵逊、?不能有任何其他類型時伤疙。在某種意義上,他們是枚舉類的擴(kuò)展:枚舉類型的值集合也是受限的辆影,但每個枚舉常量只存在?個實(shí)例徒像,?密封類的?個?類可以有可包含狀態(tài)的多個實(shí)例。

sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()

?個密封類是??抽象的蛙讥,它不能直接實(shí)例化并可以有抽象(abstract)成員锯蛀。
密封類不允許有?-private 構(gòu)造函數(shù)(其構(gòu)造函數(shù)默認(rèn)為 private)。
請注意次慢,擴(kuò)展密封類?類的類(間接繼承者)可以放在任何位置谬墙,??需在同?個?件中。

數(shù)據(jù)類

創(chuàng)建?些只保存數(shù)據(jù)的類经备。 在這些類中,?些標(biāo)準(zhǔn)函數(shù)往往是從數(shù)據(jù)機(jī)械推導(dǎo)?來的部默。在
Kotlin 中侵蒙,這叫做 數(shù)據(jù)類 并標(biāo)記為 data :

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

編譯器?動從主構(gòu)造函數(shù)中聲明的所有屬性導(dǎo)出以下成員:

  • equals() / hashCode() 對;
  • toString() 格式是 "User(name=John, age=42)" 傅蹂;
  • componentN()函數(shù) 按聲明順序?qū)?yīng)于所有屬性纷闺;
  • copy() 函數(shù)。

為了確保?成的代碼的?致性以及有意義的?為份蝴,數(shù)據(jù)類必須滿?以下要求:

  • 主構(gòu)造函數(shù)需要?少有?個參數(shù)犁功;
  • 主構(gòu)造函數(shù)的所有參數(shù)需要標(biāo)記為 val 或 var ;
  • 數(shù)據(jù)類不能是抽象婚夫、開放浸卦、密封或者內(nèi)部的;
屬性與字段

聲明一個屬性的完整語法為:

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

其初始器(initializer)案糙、getter 和 setter 都是可選.

一個只讀屬性的語法和一個可變的屬性的語法有兩方面的不同:

  • 只讀屬性用val,而可變屬性用var聲明
  • 只讀屬性不允許有setter方法

默認(rèn)的屬性的聲明為:

var name: String = "Kotlin"
        get() = field
        set(value) {
            field = value
        }
Object關(guān)鍵字

Object關(guān)鍵字定義一個類并同時創(chuàng)建一個實(shí)體:

  • 對象聲明:定義單例的方式
  • 伴生對象:可持有工廠方法及其他與類相關(guān)
  • 對象表達(dá)式:代替Java的匿名內(nèi)部類

對象表達(dá)式和對象聲明之間有?個重要的語義差別:

  • 對象表達(dá)式是在使?他們的地??即執(zhí)?(及初始化)的限嫌;
  • 對象聲明是在第?次被訪問到時延遲初始 化的;
  • 伴?對象的初始化是在相應(yīng)的類被加載(解析)時时捌,與 Java 靜態(tài)初始化器的語義相匹配怒医。
對象聲明

對象聲明將類的聲明與該類的單一實(shí)例聲明結(jié)合在一起。與普通類的實(shí)例不同奢讨,對象聲明在定義的時候就創(chuàng)建了實(shí)例稚叹。

object PayRoll {
    val allEmployees = arrayListOf<Person>()

    fun calculateSalary(){  
    }
}

可以反編譯看到:

對象聲明被編譯成通過靜態(tài)字段來持有它的單一實(shí)例的類,字段名始終為INSTANCE

public final class PayRoll {
   @NotNull
   private static final ArrayList allEmployees;
   public static final PayRoll INSTANCE;

   @NotNull
   public final ArrayList getAllEmployees() {
      return allEmployees;
   }

   public final void calculateSalary() {
   }
    //構(gòu)造函數(shù)私有
   private PayRoll() {
   }

   static {
      PayRoll var0 = new PayRoll();
       //靜態(tài)代碼塊初始化化實(shí)例對象
      INSTANCE = var0;
      boolean var1 = false;
      allEmployees = new ArrayList();
   }
}
伴生對象

Javastatic關(guān)鍵字并不是kotlin的一部分,作為替代扒袖,kotlin依賴包級別的函數(shù)和對象聲明塞茅,但是頂層函數(shù)不能訪問類的私有成員, 需要寫一個沒有類實(shí)例情況下調(diào)用但需要訪問類內(nèi)部的函數(shù),可以將其寫為類中的對象聲明的成員僚稿。

fun getFacebookName(accountId: Int) = "fb:$accountId"

class User private constructor(val nickname: String) {
    companion object {
        fun newSubscribingUser(email: String) =
            User(email.substringBefore('@'))

        fun newFacebookUser(accountId: Int) =
            User(getFacebookName(accountId))
    }
}

fun main(args: Array<String>) {
    val subscribingUser = User.newSubscribingUser("bob@gmail.com")
    val facebookUser = User.newFacebookUser(4)
    println(subscribingUser.nickname)
}

伴生對象作為普通對象凡桥,一樣可以實(shí)現(xiàn)接口和擴(kuò)展函數(shù)和屬性

data class Person(val name: String) {
    object NameComparator : Comparator<Person> {
        override fun compare(p1: Person, p2: Person): Int =
            p1.name.compareTo(p2.name)
    }
}

class Person(val firstname:String,val lastname:String){
    companion object{
        //...可空,但不能省略
    }
}
fun Person.Companion.fromJson(json:String):String{
    return json.substring(4)
}
對象表達(dá)式

object不僅可用來聲明單例對象蚀同,還可以聲明匿名對象缅刽,替代java內(nèi)部類的用法

fab.setOnClickListener(
     object : View.OnClickListener {
        override fun onClick(view: View?) {
         //....
        }
      })

當(dāng)然,也可以將其存儲到一個變量中:

val listener = object : View.OnClickListener {
    override fun onClick(p0: View?) {
       //....
    }
}

Java匿名內(nèi)部類只能擴(kuò)展一類或者實(shí)現(xiàn)一個接口蠢络,kotlin的匿名對象可以實(shí)現(xiàn)多個接口或者實(shí)現(xiàn)不同的接口衰猛。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市刹孔,隨后出現(xiàn)的幾起案子啡省,更是在濱河造成了極大的恐慌,老刑警劉巖髓霞,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卦睹,死亡現(xiàn)場離奇詭異,居然都是意外死亡方库,警方通過查閱死者的電腦和手機(jī)结序,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纵潦,“玉大人徐鹤,你說我怎么就攤上這事⊙悖” “怎么了返敬?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長寥院。 經(jīng)常有香客問我劲赠,道長,這世上最難降的妖魔是什么秸谢? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任经磅,我火速辦了婚禮,結(jié)果婚禮上钮追,老公的妹妹穿的比我還像新娘预厌。我一直安慰自己,他們只是感情好元媚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布轧叽。 她就那樣靜靜地躺著苗沧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪炭晒。 梳的紋絲不亂的頭發(fā)上待逞,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機(jī)與錄音网严,去河邊找鬼识樱。 笑死,一個胖子當(dāng)著我的面吹牛震束,可吹牛的內(nèi)容都是我干的怜庸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼垢村,長吁一口氣:“原來是場噩夢啊……” “哼割疾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嘉栓,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤宏榕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后侵佃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體麻昼,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年馋辈,在試婚紗的時候發(fā)現(xiàn)自己被綠了涌献。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡首有,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出枢劝,到底是詐尸還是另有隱情井联,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布您旁,位于F島的核電站烙常,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鹤盒。R本人自食惡果不足惜蚕脏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望侦锯。 院中可真熱鬧驼鞭,春花似錦、人聲如沸尺碰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至洛心,卻和暖如春固耘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背词身。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工厅目, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人法严。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓损敷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親渐夸。 傳聞我的和親對象是個殘疾皇子嗤锉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355

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