Kotlin也沒那么難(二)

本系列第一篇文章我們學(xué)習(xí)了kotlin的基本概念撞鹉,本篇文章我們將繼續(xù)學(xué)習(xí) 類鸠信、接口讥此、lambda以及可空性绸狐。

接口

接口聲明

interface FirstInterface {
    fun function()
}

接口實(shí)現(xiàn)

class FirstClass : FirstInterface {
    override fun function() {
    }
}

kotlin相比于java沒有 extendsimplements關(guān)鍵字,而是直接使用 : 符號(hào)


默認(rèn)方法

interface FirstInterface {
    fun function()
    fun defaultfunction() = { print("hello world") }
}

open關(guān)鍵字

kotlin默認(rèn)所有類都是final鳞滨,也就是不能被重寫洞焙、繼承
如果想讓類和方法可以被重寫、繼承需要加 open 關(guān)鍵字

open class OpenClass : FirstInterface {
    final override fun function() {
        //由override修飾的方法默認(rèn)是open拯啦,如不想被重寫需要顯式加final
    }

    open fun openFun() {
        //該方法可以被重寫
    }
}
abstract class AbsClass {
    abstract fun fun0() //強(qiáng)制子類重寫
    open fun fun1() {} //允許子類重寫
    fun fun2() {} //禁止子類重寫
}

可見修飾符

kotlin較java而言少了 默認(rèn)(即什么都不加) 修飾符澡匪,多了 internal 修飾符

internal修飾符 修飾的類和類成員模塊類可見,比如對(duì)于一個(gè)android項(xiàng)目通常有多個(gè)模塊褒链,如果a模塊的kotlin類A使用internal修飾唁情,b模塊kotlin類B就引用不到類A。但是這特性對(duì)java無效甫匹,在B模塊的java類照樣能引用類A


內(nèi)部類和嵌套類

class Outter {
    //這是靜態(tài)類 創(chuàng)建對(duì)象:Outter.Static()
    class Static
    //這是內(nèi)部類 創(chuàng)建對(duì)象:Outter().Inner()
    inner class Inner {
        fun getOutterHash() = this@Outter.hashCode()
    }
}

kotlin由于沒有 static 關(guān)鍵字甸鸟,類中聲明的類默認(rèn)就是靜態(tài)類,聲明內(nèi)部類時(shí)需要使用 inner 關(guān)鍵字
同時(shí) 內(nèi)部類引用外部類對(duì)象的方式也和java不同兵迅,需要使用 this@外部類


密封類

使用 sealed 關(guān)鍵字修飾的一個(gè)類是密封類
sealed class Student
密封類默認(rèn)是open抢韭,密封類不能被實(shí)例化
sealed類通常用在when語句中,如

sealed class Student
class Girl : Student()
class Boy : Student()
fun `when`(student: Student) {
    return when (student) {
    }
}


此時(shí)點(diǎn)擊Add remaining branches按鈕就會(huì)自動(dòng)把所有Student的子類情況列舉出來

sealed class Student
class Girl : Student()
class Boy : Student()
fun `when`(student: Student) {
    return when (student) {
        is Girl -> TODO()
        is Boy -> TODO()
    }
}

這里我們定義了一個(gè)when方法恍箭,因?yàn)楹拖到y(tǒng)的when關(guān)鍵字重名刻恭,所以需要使用兩個(gè)`符號(hào)轉(zhuǎn)義


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

class People constructor(name: String, val age: Int) {
    var name: String = ""

    init {
        this.name = name
    }
}
class People(var name: String, val age: Int)

要點(diǎn):

  1. 以上兩段代碼編譯結(jié)果一模一樣,類名旁的括號(hào)聲明了默認(rèn)構(gòu)造函數(shù)(也叫主構(gòu)造函數(shù))
  2. 如果在構(gòu)造函數(shù)中聲明參數(shù)時(shí)有使用val和var則會(huì)自動(dòng)把該參數(shù)設(shè)置為成員變量(第二段代碼兩個(gè)參數(shù)都被編譯器默認(rèn)設(shè)置為同的名成員變量)扯夭,如果沒有則只當(dāng)成一個(gè)臨時(shí)變量:主構(gòu)造函數(shù)執(zhí)行完后該變量就訪問不到
  3. init 關(guān)鍵字可以引導(dǎo)一個(gè)語句塊鳍贾,該語句塊伴隨主構(gòu)造函數(shù)執(zhí)行,所以第一段代碼的init語句塊中可以訪問到name這個(gè)臨時(shí)變量
  4. 一個(gè)類可以有多個(gè)init語句塊交洗,順序是從上到下依次執(zhí)行
  5. 如果主構(gòu)造函數(shù)沒有注解和可見性修飾符則可以去掉constructor 關(guān)鍵字
  6. 所有聲明的從構(gòu)造函數(shù)最終都必須要執(zhí)行主構(gòu)造函數(shù)贾漏,不管是直接還是間接
open class Button() {
    constructor(x: Float, y: Float) : this() {} //直接調(diào)用無參的主構(gòu)造函數(shù)
    constructor(x: Float) : this(x, 0F) {} //通過另一兩個(gè)參數(shù)的從構(gòu)造函數(shù)間接實(shí)現(xiàn)主構(gòu)造函數(shù)
}
  1. 子類定義構(gòu)造函數(shù)時(shí)一定要實(shí)現(xiàn)父類的構(gòu)造函數(shù)
open class Button
class MyButton1 : Button {
    constructor() : super() //定義構(gòu)造函數(shù)
}
class MyButton2() : Button() //定義構(gòu)造函數(shù)

Data class

DataClass其實(shí)只要在定義的class前加一個(gè)data關(guān)鍵字就好
唯一要求就是主構(gòu)造函數(shù)至少有一個(gè)字段

data class MyData(var name:String)

使用DataClass聲明類時(shí)編譯器會(huì)自動(dòng)為該類重寫hashCode()equals()藕筋、copy()toString()等方法纵散,很方便梳码。


Object class

ObjectClass極大的簡化了單例的聲明

object SingleDb

這里我們定義了一個(gè)SingleDb類,他是一個(gè)單例伍掀,不能實(shí)例化掰茶,所以我們也無法指定構(gòu)造函數(shù)
我們可以看看編譯成java文件是什么樣子

public final class SingleDb {
   public static final SingleDb INSTANCE;

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

伴生對(duì)象(Companion)

kotlin是沒有static關(guān)鍵字的,所以沒有靜態(tài)變量與靜態(tài)方法
但是我們可以使用伴生對(duì)象實(shí)現(xiàn)

class SingleDb {
    companion object Db {
        var dbName: String = "example"
    }
}

fun test() {
    //兩種寫法都行蜜笤,但是Java中只能使用第一種濒蒋,原因看編譯后的java文件就明白了
    print(SingleDb.Db.dbName)
    print(SingleDb.dbName)
}

我們使用companion object + 伴生對(duì)象名字 + 語句塊的方式聲明了一個(gè)伴生對(duì)象,其中伴生對(duì)象名字可以省略把兔,這樣編譯器會(huì)默認(rèn)賦予一個(gè)COMPANION的名字
編譯后java文件

public final class SingleDb {
   private static String dbName = "example";
   public static final SingleDb.Db Db = new SingleDb.Db();

   public static final class Db {
      public final String getDbName() {
         return SingleDb.dbName;
      }

      public final void setDbName(String var1) {
         SingleDb.dbName = var1;
      }

      private Db() {
      }
   }
}



可空類型

kotlin最為人津津樂道的就是不會(huì)產(chǎn)生空指針異常
其實(shí)說白了就是編譯期檢查代碼沪伙,把可能會(huì)出現(xiàn)異常的地方全部編譯失敗,丟給開發(fā)一個(gè)個(gè)去解決
我們可以在任何聲明類型的時(shí)候給該類型后加一個(gè)?來告訴編譯器這是可空的類型(可以為null)县好,如果沒加?號(hào)表示這是不可空的围橡,那么如果你想給不可空的對(duì)象賦值null是一定編譯不過去的!!!

class People {
    val name: String? = null
    val region: String = "china"
}

比如上面的people類我們就定義name可以為空,region不能為空
同樣的缕贡,我們可以看看編譯后的java文件

public final class People {
   @Nullable
   private final String name;
   @NotNull
   private final String region = "china";

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

   @NotNull
   public final String getRegion() {
      return this.region;
   }
}

可以看到其實(shí)是通過java的@Nullable@NotNull注解實(shí)現(xiàn)的

有時(shí)候我們明明知道該值此時(shí)不為空翁授,但是由于聲明為可空類型我們得判空后才能操作,此時(shí)可以直接使用!!操作符調(diào)用晾咪,如 print(people!!name)收擦,使用!!就是告訴編譯器不用檢查這里的可控性,當(dāng)然如果運(yùn)行的時(shí)候people對(duì)象為空就會(huì)直接拋出空指針異常谍倦。
所以通常使用了kotlin還是老空指針就是濫用!!操作符的原因了




Kotlin中的Lambda表達(dá)式

還剩一些篇章塞赂,我們就來了解下kotlin中的lambda表達(dá)式吧
lambad是一個(gè)很簡單的小語法,這里貼出一個(gè)教程鏈接大家可以自行閱讀
lambda教程
這里我們主要講lambda在kotlin中的用處

fun sayHello(name: String, onSayHelloFinished: () -> Unit) {
    print("hello $name")
    onSayHelloFinished()
}

以上是一個(gè)最最最直觀的例子
Unit其實(shí)就類似java中的Void
其中String() -> Unit都是類型昼蛀,前者是String類型宴猾,后者是函數(shù)類型!
()->Unit定義了一個(gè)函數(shù)類型曹洽,該類型的對(duì)象是一個(gè)函數(shù)鳍置,該函數(shù)的參數(shù)在括號(hào)內(nèi)(本例無參數(shù))辽剧,返回值是Unit類型
本例聲明了該函數(shù)類型的函數(shù)對(duì)象onSayHelloFinished送淆,該對(duì)象的使用很簡單:
onSayHelloFinished()onSayHelloFinished.invoke() ,兩種方式都可以調(diào)用
有了函數(shù)類型這一語法糖怕轿,至少本人自定義view時(shí)不用再寫各種接口來提供點(diǎn)擊事件了

偽代碼獻(xiàn)上:

class MyView {
    var clickCallBack: ((MyView) -> Boolean)? = null
    fun setListener(onClick: (MyView) -> Boolean) {
        clickCallBack = onClick
    }

    fun clickTwice() {
        if (clickCallBack != null) {
            clickCallBack.invoke(this)
        }
    }
}



查看編譯后java文件

學(xué)習(xí)kotlin最好的方法就是一邊寫偷崩,一邊想編譯后的class文件是怎樣的
AndroidStudio自帶了一個(gè)很好的工具用于查看編譯后的class文件
步驟1:打開一個(gè)kt文件,注意焦點(diǎn)要在文件內(nèi)撞羽,也就是文件內(nèi)要顯示一閃一閃的光標(biāo)
步驟2:如圖阐斜,點(diǎn)擊Show Kotlin Bytecode


步驟3: 點(diǎn)擊Decompile,搞定诀紊!




結(jié)語

通過本篇文章的學(xué)習(xí)我們已經(jīng)算 掌握kotlin 了谒出,基本上加上一篇文章可以應(yīng)對(duì)一般的開發(fā)需求,之后會(huì)有番外篇講Kotlin的反射、泛型以及委托等笤喳。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末为居,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子杀狡,更是在濱河造成了極大的恐慌蒙畴,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呜象,死亡現(xiàn)場離奇詭異膳凝,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)恭陡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門蹬音,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人子姜,你說我怎么就攤上這事祟绊。” “怎么了哥捕?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵牧抽,是天一觀的道長。 經(jīng)常有香客問我遥赚,道長扬舒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任凫佛,我火速辦了婚禮讲坎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘愧薛。我一直安慰自己晨炕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布毫炉。 她就那樣靜靜地躺著瓮栗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瞄勾。 梳的紋絲不亂的頭發(fā)上费奸,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音进陡,去河邊找鬼愿阐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛趾疚,可吹牛的內(nèi)容都是我干的缨历。 我是一名探鬼主播以蕴,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼辛孵!你這毒婦竟也來了舒裤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤觉吭,失蹤者是張志新(化名)和其女友劉穎腾供,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鲜滩,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伴鳖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了徙硅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榜聂。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嗓蘑,靈堂內(nèi)的尸體忽然破棺而出须肆,到底是詐尸還是另有隱情,我是刑警寧澤桩皿,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布豌汇,位于F島的核電站,受9級(jí)特大地震影響泄隔,放射性物質(zhì)發(fā)生泄漏拒贱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一佛嬉、第九天 我趴在偏房一處隱蔽的房頂上張望逻澳。 院中可真熱鬧,春花似錦暖呕、人聲如沸斜做。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓤逼。三九已至,卻和暖如春钝腺,著一層夾襖步出監(jiān)牢的瞬間抛姑,已是汗流浹背赞厕。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國打工艳狐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人皿桑。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓毫目,卻偏偏與公主長得像蔬啡,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子镀虐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348