Kotlin基礎(chǔ)全教程

Android為啥要從Java轉(zhuǎn)向Kotlin

Kotlin 是一種在 Java 虛擬機(jī)上運(yùn)行的靜態(tài)類型編程語(yǔ)言,被稱之為 Android 世界的Swift,由 JetBrains 設(shè)計(jì)開發(fā)并開源。蘋果公司已經(jīng)在用Swift語(yǔ)言替代Object-C語(yǔ)言,Google也找到了替代Java的語(yǔ)言,也就是JetBrains公司(Android Studio也是用該公司的Intelli J改的)主推的Kotlin。現(xiàn)在將kotlin作為了編寫Android的官方語(yǔ)言,而后會(huì)有越來(lái)越多的項(xiàng)目使用Kotlin。Kotlin有了強(qiáng)有力的親爸JetBrains公司和Google這個(gè)干爸,自然被捧在手心了。

Kotlin在編寫代碼時(shí)有如下優(yōu)勢(shì):

  • 代碼簡(jiǎn)潔高效
  • Android Jetpack 與其他庫(kù)中的 Kotlin 支持
  • 可與 Java 的一起使用
  • 空指針安全

同樣,kotlin支持函數(shù)式編程、支持lambda表達(dá)式、流式API...,此處略去一萬(wàn)字。總之,Java是上世紀(jì)的編程語(yǔ)言了,當(dāng)你學(xué)過(guò)了Kotlin之后 ,你會(huì)發(fā)現(xiàn),之前寫過(guò)那么多的代碼都是在浪費(fèi)生命。

語(yǔ)法基礎(chǔ)

變量
    var age: Int = 0
    var num = 20
    val TYPE: Int = 100  
    var name: String = ""  

寫法形如:var str: String
用var或val聲明,前面是參數(shù)名,后面是參數(shù)的類型,中間用:分割

var即英文“variable”的意思,聲明成一個(gè)可變變量。val即英文“value”的意思,申明的是一個(gè)不可變變量,對(duì)應(yīng)的java里面的final。

kotlin中有優(yōu)秀的類推導(dǎo)機(jī)制,age聲明了變量類型,num沒(méi)有聲明變量類型,但是依然可以通過(guò),是由Kotlin自動(dòng)推導(dǎo)了類型。

常量

const
如果只讀屬性的值在編譯期是已知的,那么可以使用 const 修飾符將其標(biāo)記為編譯期常量,相當(dāng)于java中的public static final修飾。

    //const val 需與 companion object 搭配使用
    companion object {
        const val VERSION = 1
    }
空指針檢查

在Kotlin里,可以用“?”表示可以為空,也可以用“!!”表示不可以為空。
給變量加上?標(biāo)識(shí),會(huì)通告所有使用該變量的地方,必須給出為空的補(bǔ)救措施。

     var info: String? = null

        println(info?.length)  //第一種補(bǔ)救:如果info為null,就不執(zhí)行后面的.length代碼

        println(info!!.length)  //第二種補(bǔ)救:這里如果為null,我自己負(fù)責(zé)info,會(huì)報(bào)出空指針,這種處理需慎用

        if (info != null) {   //第三種補(bǔ)救措施,如下這種同java寫法
            println(info.length)
        }

      println(info?.length ?: "空數(shù)據(jù)")  //第四種補(bǔ)救措施,如果真的為null,則改為返回"空數(shù)據(jù)"

第二個(gè)檢查方式是let函數(shù),?.當(dāng)然能解決大部分問(wèn)題,但是user每使用一次?.相當(dāng)于都加上了代碼if(user != null),實(shí)際上在同一個(gè)函數(shù)中,我們只需要做一次非空判斷就行了,這就是我們的let函數(shù)的作用,在代碼塊內(nèi)容用it代替該對(duì)象。

user?.let {
            it.login()
            it.logout()
        }
可見性修飾符

在 Kotlin 中有這四個(gè)可見性修飾符:private、 protected、 internal 和 public。 如果沒(méi)有顯式指定修飾符的話,默認(rèn)可見性是 public。
internal:在本模塊內(nèi)可見

字符串拼接

將變量拼接在字符串之內(nèi),用${}來(lái)包裹變量即可

    fun StringFormat(title: String) = {
        "這里是拼接字符串${title}"
    }

字符串自動(dòng)換行:

val content = """
            哈哈哈哈
            呵呵呵呵
            嘿嘿嘿嘿
        """.trimIndent()
值比較、賦值:
        println(name1.equals(name2))  //同java的equals

        println(name1 == name2)  //同equals作用,比較值的相等,這里為true

        println(name1 === name2)  //比較地址是否相等,即比較是否為同一個(gè)對(duì)象,這里為false

    val name1 = "lili"
    val name2 = "lili"
    println(name1 === name2) //這里返回ture,因?yàn)樽址诔A砍厥菑?fù)用一份的

將條件判斷的結(jié)果賦值:

        val num1 = 100
        val num2 = 101
        val max = if (num1 > num2) num1 else num2
when

用when關(guān)鍵字代替java中的switch使用,when作為判斷,條件可為任意類型

fun whenUse(obj: Any, type: Int) {
        when(obj) {
            1 ->
                ""
            in 2..5 ->
                ""
            is String ->
                ""
            else ->
                ""
        }
    }

val week = 5
        val info = when(week) {
            1 -> "星期一"
            1 -> "星期二"
            3 -> println()
            4 -> 5
            -1 -> TODO()  //Nothing類型:表示未實(shí)現(xiàn)的功能,會(huì)拋出異常 public inline fun TODO(): Nothing = throw NotImplementedError()
            else -> ""  //返回String類型,info一定是String類型
        }

//將when的返回值直接使用起來(lái)賦值:
        val info2 = when(week) {
            1 -> "星期一"
            2 -> "星期二"
            3 -> println()
            4 -> true
            in 2..5 ->
                ""
            else -> {  //必須要有else
                //else 返回括號(hào),info2就可以是任意類型 Any
            }
        }
Any

java中所有類的父類是Object,而Kotlin中所有類的父類為Any。

object

Kotlin中沒(méi)有大寫的Object了,而是有小寫的object,表示單例。

class SingleTon {
    object Holder {
        var instance = SingleTon()
    }
}
is

Kotlin中用is代碼java中的instanceOf 來(lái)判斷類型

 fun charge() {
        var type = ""
        if (type is String) {  //instanceOf

        }
    }
in使用
  • in來(lái)檢查一個(gè)值是否在一個(gè)區(qū)間內(nèi)
fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'   //檢查字符是否為字母
  • 檢查list中是否有元素name
    fun inList() {
        val names = listOf("lala", "haha", "yaya")
        if("lili" in names) {

        }
    }
takeIf,takeUnless函數(shù)
        user.name.takeIf {
            TextUtils.isEmpty(it)
        }.let {
            print(it)
        }

        user.name.takeUnless {
            !TextUtils.isEmpty(it)
        }.let {
            print(it)
        }

takeIf的閉包返回一個(gè)判斷結(jié)果,如果為false時(shí)takeIf函數(shù)返回null;takeUnless與takeIf相反,為true時(shí)takeUnless函數(shù)返回null。

使用場(chǎng)景:只需要單個(gè)if分支語(yǔ)句的時(shí)候
優(yōu)點(diǎn):
可以配合其他作用域函數(shù)返回的結(jié)果,做出單向判斷,保持鏈?zhǔn)秸{(diào)用
簡(jiǎn)化寫法,邏輯清晰,減少代碼量,代碼更優(yōu)雅

init

主構(gòu)造函數(shù)里不能寫代碼,那我們?cè)趺闯跏蓟@個(gè)類的代碼呢?Kotlin則是提供了初始化模塊,基本上就是用init修飾符修飾一個(gè){},在類初始化時(shí)執(zhí)行這段代碼。

    class User() {
        init {

        }
    }

數(shù)組

創(chuàng)建數(shù)組
        var names = arrayOf("小王", "小花", "小紅", "小明")

        var ages = arrayOf(22, 19, 18)

        var array = arrayOfNulls<String>(5)   //創(chuàng)建空數(shù)組
        array.set(1, "哈哈")   //賦值
數(shù)組遍歷
    fun arrayForEach() {
        var names = arrayOf("小王", "小花", "小紅", "小明")
        for (i in 1..5 step 1) {   //跳過(guò)第1步遍歷

        }

        for (i in 0..names.size-1) {  //遍歷names數(shù)組,相當(dāng)于java里  for (int i=0:i<names.length;i++)

        }

        for (i in names.indices) {  //正序遍歷

        }

        for (i in 1 until 10){    //為一個(gè)左閉右開的區(qū)間,打印1到9
            print("$i")
        }

        for (i in names.size downTo 0) {  //倒序遍歷

        }

        for (i in names.reversed()) {  //反轉(zhuǎn)遍歷數(shù)組

        }

        repeat(10) {  //打印0到9
            print(it)
        }

        names.forEach {  //forEach函數(shù)遍歷,Kotlin里的集合都自帶foreach函數(shù)

        }

        names.forEachIndexed { index, s ->   //帶position的遍歷
            
        }
    }

in的用法:val num = 1..10,表示變量num是一個(gè)[1,10]的區(qū)間。
step表步長(zhǎng),表示遍歷間隔的個(gè)數(shù)。

類與對(duì)象

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

在 Kotlin 中的一個(gè)類可以有一個(gè)主構(gòu)造函數(shù)以及一個(gè)或多個(gè)次構(gòu)造函數(shù)。如果主構(gòu)造函數(shù)沒(méi)有任何注解或者可見性修飾符,可以省略這個(gè) constructor 關(guān)鍵字。

class User constructor(name: String, age: Int, id: Int)

class User(name: String, age: Int, id: Int)

操作示例:

class User(val name: String) { //主構(gòu)造函數(shù)
    var age = 0
    var sex = "man"

    /**
     * 有參次構(gòu)造方法
     */
    constructor(name: String, age: Int, id: Int): this(name) {  //次構(gòu)造函數(shù),必須去調(diào)用主構(gòu)造

    }
    constructor(name: String, sex: String): this(name) {
        this.sex = sex
    }

    /**
     * 無(wú)參次構(gòu)造方法
     */
    constructor(): this("暫無(wú)名字") {

    }

    init {
        println("name is $name")
        println("age is $age")
    }

    fun main() {
        User()   //次構(gòu)造
        User("小明")  //主構(gòu)造
        User("小明", 18, 1)  //次構(gòu)造
    }
}
創(chuàng)建對(duì)象

Kotlin中創(chuàng)建對(duì)象不再需要關(guān)鍵字new了,直接創(chuàng)建對(duì)象:

var user = User()
繼承

在Kotlin中的類和方法默認(rèn)是不可以被繼承的,需要加上關(guān)鍵字open,才允許被繼承。繼承用":"表示,類和接口的繼承都用":",類需要加()。

class VipUser : User(), Use {}

open class User {}

interface Use {}
內(nèi)部類
class OuterClass {

    val outerInfo: String = "outerInfo"

    fun show() {
        InnerClass().show() //外部類可以訪問(wèn)內(nèi)部類
    }

    //加上inner關(guān)鍵字才是內(nèi)部類,內(nèi)部類才能和外部類相互訪問(wèn),否則不是內(nèi)部類而是嵌套類
    inner class InnerClass {
        fun show() = println("my outerclass info: " + outerInfo) //內(nèi)部類也可以訪問(wèn)外部類
    }
}
數(shù)據(jù)類

即為我們通常使用的bean類,一般在定義數(shù)據(jù)類的時(shí)候,我們需要手動(dòng)實(shí)現(xiàn)這個(gè)類的equals()、hashcode()、toString()等方法,這些方法是必要的。但是加上data關(guān)鍵字后,該類會(huì)自動(dòng)生成這些方法。

data class User(var name: String) {

}
單例類

在java中,單例的寫法需要自己手動(dòng)實(shí)現(xiàn),比如用懶漢式還是餓漢式。但是在Kotlin中單例就非常簡(jiǎn)化了,只需要將class替換成object就可以了。

    object SingleTonClass {

        var params: String? = null

        fun function() {

        }
    }

    SingleTonClass.function()

    SingleTonClass.params

使用單例時(shí)不需要再獲取instance實(shí)例對(duì)象了,Kotlin已經(jīng)在內(nèi)部幫我們創(chuàng)建了一個(gè)單例的實(shí)例,直接使用類名調(diào)用它的屬性和方法。

compaion object(伴生對(duì)象)

在java中static表靜態(tài)變量,kotlin中取消了static,用compaion object來(lái)代替它的用法。

class Companionobject {

    //伴生對(duì)象的由來(lái),是kotlin沒(méi)有static靜態(tài)
    //不管對(duì)象創(chuàng)建多少個(gè),companion object只會(huì)初始化一次
    companion object {
        val info = "lili" //靜態(tài)變量

        fun showInfo() {
            println(info)
        }
    }

    fun test() {
        //背后代碼:生成了 Companionobject.companion類
        println(info)

        showInfo()
    }
}
Kotlin 接口
//kotlin的接口
//接口里面的所有成員和方法和接口本身都是 public open的
//實(shí)現(xiàn)類不僅要重寫接口的函數(shù),還要重寫接口的成員
interface IUSB {
    var usbVersionInfo: String

    fun insertUSB(): String
}

//成員重寫寫到實(shí)現(xiàn)類的構(gòu)造方法中 方式
class Mouse(override var usbVersionInfo: String = "USB 3.0") : IUSB {
    override fun insertUSB(): String = usbVersionInfo
}

//成員重寫寫到實(shí)現(xiàn)類中的方式
class KeyBorad: IUSB {
    override var usbVersionInfo: String = "USB 3.0"
        get() {  //get/set 方法會(huì)調(diào)用代碼塊內(nèi)容,并返回一個(gè)值
            println("獲取了 usbVersionInfo ${usbVersionInfo}")
            return field
        }
        set(value) {
            println("設(shè)置了 usbVersionInfo ${usbVersionInfo}")
            field = value //field表示該 usbVersionInfo 屬性
        }

    override fun insertUSB(): String = "keyboard $usbVersionInfo"

}

fun showInterface() {
    val usb1 = Mouse()
    usb1.insertUSB()

    val usb2 = KeyBorad()
    usb2.usbVersionInfo = "sfd" //使用屬性調(diào)用get()  賦值屬性調(diào)用set()
    usb2.insertUSB()
}
JvmField JvmStatic
// JvmStatic JvmField注解使用
//用于 Java調(diào)用kotlin 屬性和方法 兼容性 而產(chǎn)生的
//JvmField注解使用 將默認(rèn)的private修改成了public訪問(wèn)符
//JvmStatic 將 companion object 中的 屬性、方法能像kotlin一樣能直接調(diào)用
class Personz {
    @JvmField
    val names = listOf("lala", "haha", "yaya")

    //這個(gè) names轉(zhuǎn)成java就是如下代碼, val修飾的 names屬性成了 private final ,外部調(diào)用不到,而提供了一個(gè)方法獲取
    // 使用了 @JvmField注解,將該屬性從private變成public,能直接獲取val修飾的屬性
    /**
     * public final class Personz {
     *    @NotNull
     *    private final List names = CollectionsKt.listOf(new String[]{"lala", "haha", "yaya"});
     *
     *    @NotNull
     *    public final List getNames() {
     *       return this.names;
     *    }
     * }
     */

    companion object {
        @JvmField
        val name : String = "lala"

        @JvmStatic
        fun printlnPersonName() = println("名字: " + name)
    }
}

//java中調(diào)用代碼
    public void getJvmFieldStatic() {
//        new Personz().getNames();  //沒(méi)加 jvmField的調(diào)用
        List<String> names = new Personz().names; //加了 jvmField的調(diào)用

        //沒(méi)加 JvmStatic,java調(diào)用 Companion object需要加一層 Companion類來(lái)調(diào)用
//        Personz.Companion.getName();
//        Personz.Companion.printlnPersonName();

        String name = Personz.name;
        //加上 JvmStatic,就有了和Kotlin一樣的調(diào)用效果,把函數(shù)寫到了 Companion類 外面
        Personz.printlnPersonName();
    }
range符
    fun range(number: Int) {
        //range 范圍
        if (number in 10..59) {
            println("不及格")
        } else if (number in 0 .. 9) {
            println("很差")
        } else if (number in 60..100) {
            println("及格")
        } else {
            println("分?jǐn)?shù)不合法")
        }
    }
將現(xiàn)有Java文件轉(zhuǎn)換為Kotlin文件

右鍵->Convert Java File to Kotlin File
我們可以將現(xiàn)有Java文件代碼轉(zhuǎn)換為Kotlin文件代碼,而實(shí)現(xiàn)從java往kotlin上轉(zhuǎn)移。可以針對(duì)單個(gè)文件,某個(gè)包,某個(gè)module,或者整個(gè)項(xiàng)目轉(zhuǎn)為kotlin文件。
轉(zhuǎn)換完成之后可能會(huì)有少量語(yǔ)法錯(cuò)誤,需要手動(dòng)修改一下。

將kotlin代碼轉(zhuǎn)為java

在Tools->Kotlin->show Kotlin ByteCode,點(diǎn)擊Decompile轉(zhuǎn)回類似Java代碼。

教程參考:

https://www.bilibili.com/video/BV1kT4y1o7nP

Github代碼地址:

https://github.com/running-libo/KotlinPractise

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。