kotlin基礎(chǔ)語法

最近在學(xué)kotlin,這是本人看菜鳥教程后所做的筆記,很多會(huì)內(nèi)容和菜鳥教程重復(fù),僅供參考

基礎(chǔ)語法

包聲明

//類在包的位置
package com.demo.main

import java.util.*

函數(shù)定義

函數(shù)定義使用關(guān)鍵字 fun,參數(shù)格式為:參數(shù) : 類型

fun printSum(a: Int,b: Int){
    println(a+b)
}

無返回值的函數(shù)

public fun printSum(a: Int, b: Int) { 
    print(a + b)
}

可變長參數(shù)函數(shù)

fun vars(vararg a:String){
    for(str in a){
        println(str)
    }
}

定義常量與變量

可變變量定義:var 關(guān)鍵字

var <標(biāo)識(shí)符> : <類型> = <初始化值>

不可變變量定義:val關(guān)鍵字,只能賦值一次的變量Java中final修飾的變量)

val <標(biāo)識(shí)符> : <類型> = <初始化值>

注釋

// 這是一個(gè)單行注釋

/* 這是一個(gè)多行的
塊注釋。 */

字符串模板

$ 表示一個(gè)變量名或者變量值

$varName 表示變量值

${varName.fun()} 表示變量的方法返回值:

數(shù)據(jù)類型

類型 位寬度
Double 64
Float 32
Long 64
Int 32
Short 16
Byte 8

比較兩個(gè)數(shù)字

在 Kotlin 中,三個(gè)等號(hào) === 表示比較對(duì)象地址,兩個(gè) == 表示比較兩個(gè)值大小。

類型轉(zhuǎn)換

由于不同的表示方式,較小類型并不是較大類型的子類型,較小的類型不能隱式轉(zhuǎn)換為較大的類型。 這意味著在不進(jìn)行顯式轉(zhuǎn)換的情況下我們不能把 Byte 型值賦給一個(gè) Int 變量。

val b: Byte = 10 // OK, 字面值是靜態(tài)檢測的
val i: Int = b // 錯(cuò)誤

我們可以代用其toInt()方法。

val b: Byte = 1 // OK, 字面值是靜態(tài)檢測的
val i: Int = b.toInt() // OK

每種數(shù)據(jù)類型都有下面的這些方法,可以轉(zhuǎn)化為其它的類型:

toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char

有些情況下也是可以使用自動(dòng)類型轉(zhuǎn)化的,前提是可以根據(jù)上下文環(huán)境推斷出正確的數(shù)據(jù)類型而且數(shù)學(xué)操作符會(huì)做相應(yīng)的重載。例如下面是正確的:

val l = 1L + 3 // Long + Int => Long

條件控制

IF 表達(dá)式

一個(gè) if 語句包含一個(gè)布爾表達(dá)式和一條或多條語句。

var max = a 
if (a < b) max = b

// 使用 else 
var max: Int
if (a > b) {
    max = a
} else {
    max = b
}
 
// 作為表達(dá)式
val max = if (a > b) a else b

When 表達(dá)式

when最簡單的形勢(shì)如下:

fun whenTest(a : Int){
    when(a){
        1->{
           println(1)
        }
        2->{
            println(2)
        }
        3->{
            println(3)
        }
        else->{
            println("其他")
        }
    }
}

when 也可以用來取代 if-else if鏈。 如果不提供參數(shù),所有的分支條件都是簡單的布爾表達(dá)式,而當(dāng)一個(gè)分支的條件為真時(shí)則執(zhí)行該分支:

when {
    x.isOdd() -> print("x is odd")
    x.isEven() -> print("x is even")
    else -> print("x is funny")
}

循環(huán)控制

For 循環(huán)

循環(huán)體可以是一個(gè)代碼塊:

for (item: Int in ints) {
    // ……
}

while 與 do...while 循環(huán)

while是最基本的循環(huán),它的結(jié)構(gòu)為:

while( 布爾表達(dá)式 ) {
  //循環(huán)內(nèi)容
}

do…while 循環(huán) 對(duì)于 while 語句而言,如果不滿足條件,則不能進(jìn)入循環(huán)。但有時(shí)候我們需要即使不滿足條件,也至少執(zhí)行一次。

do…while 循環(huán)和 while 循環(huán)相似,不同的是,do…while 循環(huán)至少會(huì)執(zhí)行一次。

do {
       //代碼語句
}while(布爾表達(dá)式);

返回和跳轉(zhuǎn)

Kotlin 有三種結(jié)構(gòu)化跳轉(zhuǎn)表達(dá)式:

  • return。默認(rèn)從最直接包圍它的函數(shù)或者匿名函數(shù) 返回。
  • break。終止最直接包圍它的循環(huán)。
  • continue。繼續(xù)下一次最直接包圍它的循環(huán)。

類和對(duì)象

類定義

Kotlin 中使用關(guān)鍵字 class 聲明類,后面緊跟類名:

class Runoob {  // 類名為 Runoob
    // 大括號(hào)內(nèi)是類體構(gòu)成
}

也可以定義一個(gè)空類:

class Empty

屬性定義

類的屬性可以用關(guān)鍵字 var 聲明為可變的,否則使用只讀關(guān)鍵字 val 聲明為不可變。

我們可以像使用普通函數(shù)那樣使用構(gòu)造函數(shù)創(chuàng)建類實(shí)例:

val site = Runoob() // Kotlin 中沒有 new 關(guān)鍵字

要使用一個(gè)屬性,只要用名稱引用它即可

site.name           // 使用 . 號(hào)來引用
site.url

主構(gòu)造器

主構(gòu)造器中不能包含任何代碼,初始化代碼可以放在初始化代碼段中,初始化代碼段使用 init 關(guān)鍵字作為前綴。

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

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

類也可以有二級(jí)構(gòu)造函數(shù),需要加前綴 constructor:

class Person { 
    constructor(parent: Person) {
        parent.children.add(this) 
    }
}

如果類有主構(gòu)造函數(shù),每個(gè)次構(gòu)造函數(shù)都要,或直接或間接通過另一個(gè)次構(gòu)造函數(shù)代理主構(gòu)造函數(shù)。在同一個(gè)類中代理另一個(gè)構(gòu)造函數(shù)使用 this 關(guān)鍵字:

class Person(val name: String) {
    constructor (name: String, age:Int) : this(name) {
        // 初始化...
    }
}

抽象類

抽象是面向?qū)ο缶幊痰奶卣髦?,類本身,或類中的部分成員,都可以聲明為abstract的。抽象成員在類中不存在具體的實(shí)現(xiàn)。

abstract class father{
     abstract fun f()
}

class childs : father() {
    override fun f() {

    }
}

嵌套類

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)部類

內(nèi)部類使用 inner 關(guān)鍵字來表示。

內(nèi)部類會(huì)帶有一個(gè)對(duì)外部類的對(duì)象的引用,所以內(nèi)部類可以訪問外部類成員屬性和成員函數(shù)。

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)
        }
    }
}

fun main(args: Array<String>) {
    val demo = Outer().Inner().foo()
    println(demo) //   1
    val demo2 = Outer().Inner().innerTest()   
    println(demo2)   // 內(nèi)部類可以引用外部類的成員,例如:成員屬性
}

匿名內(nèi)部類

使用對(duì)象表達(dá)式來創(chuàng)建匿名內(nèi)部類:

class Test {
    var v = "成員屬性"

    fun setInterFace(test: TestInterFace) {
        test.test()
    }
}

/**
 * 定義接口
 */
interface TestInterFace {
    fun test()
}

fun main(args: Array<String>) {
    var test = Test()

    /**
     * 采用對(duì)象表達(dá)式來創(chuàng)建接口對(duì)象,即匿名內(nèi)部類的實(shí)例。
     */
    test.setInterFace(object : TestInterFace {
        override fun test() {
            println("對(duì)象表達(dá)式創(chuàng)建匿名內(nèi)部類的實(shí)例")
        }
    })
}

類的修飾符

類的修飾符包括 classModifier 和accessModifier:

  • classModifier: 類屬性修飾符,標(biāo)示類本身特性。
abstract    // 抽象類  
final       // 類不可繼承,默認(rèn)屬性
enum        // 枚舉類
open        // 類可繼承,類默認(rèn)是final的
annotation  // 注解類
  • accessModifier: 訪問權(quán)限修飾符
private    // 僅在同一個(gè)文件中可見
protected  // 同一個(gè)文件中或子類可見
public     // 所有調(diào)用的地方都可見
internal   // 同一個(gè)模塊中可見

繼承

Kotlin 中所有類都繼承該 Any 類,它是所有類的超類,對(duì)于沒有超類型聲明的類是默認(rèn)超類:

class Example // 從 Any 隱式繼承

Any 默認(rèn)提供了三個(gè)函數(shù):

equals()

hashCode()

toString()

注意:Any 不是 java.lang.Object。

如果一個(gè)類要被繼承,可以使用 open 關(guān)鍵字進(jìn)行修飾。

open class father(p: Int)           // 定義基類

class Derived(p: Int) : father(p)

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

如果子類有主構(gòu)造函數(shù), 則基類必須在主構(gòu)造函數(shù)中立即初始化。

open class Person(var name : String, var age : Int){// 基類

}

class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) {

}

// 測試
fun main(args: Array<String>) {
    val s =  Student("Runoob", 18, "S12346", 89)
    println("學(xué)生名: ${s.name}")
    println("年齡: ${s.age}")
    println("學(xué)生號(hào): ${s.no}")
    println("成績: ${s.score}")
}

子類沒有主構(gòu)造函數(shù)

如果子類沒有主構(gòu)造函數(shù),則必須在每一個(gè)二級(jí)構(gòu)造函數(shù)中用 super 關(guān)鍵字初始化基類,或者在代理另一個(gè)構(gòu)造函數(shù)。初始化基類時(shí),可以調(diào)用基類的不同構(gòu)造方法。

/**用戶基類**/
open class Person(name:String){
    /**次級(jí)構(gòu)造函數(shù)**/
    constructor(name:String,age:Int):this(name){
        //初始化
        println("-------基類次級(jí)構(gòu)造函數(shù)---------")
    }
}

/**子類繼承 Person 類**/
class Student:Person{

    /**次級(jí)構(gòu)造函數(shù)**/
    constructor(name:String,age:Int,no:String,score:Int):super(name,age){
        println("-------繼承類次級(jí)構(gòu)造函數(shù)---------")
        println("學(xué)生名: ${name}")
        println("年齡: ${age}")
        println("學(xué)生號(hào): ${no}")
        println("成績: ${score}")
    }
}

接口

Kotlin 接口與 Java 8 類似,使用 interface 關(guān)鍵字定義接口,允許方法有默認(rèn)實(shí)現(xiàn):

interface MyInterface{
    fun bar()
    fun foo(){
        println("foo")
    }
}

class MyClass : MyInterface{
    override fun bar() {
        println("bar")
    }
}

fun main(args:Array<String>){
    var myClass=MyClass()
    myClass.bar()
    myClass.foo()
}

接口中的屬性

接口中的屬性只能是抽象的,不允許初始化值,接口不會(huì)保存屬性值,實(shí)現(xiàn)接口時(shí),必須重寫屬性:

interface MyInterface{
    var name:String
}

class MyClass : MyInterface{
    override var name="Jack"
}

fun main(args:Array<String>){
    var myClass=MyClass()
    println(myClass.name)
}

函數(shù)重寫

實(shí)現(xiàn)多個(gè)接口時(shí),可能會(huì)遇到同一方法繼承多個(gè)實(shí)現(xiàn)的問題。例如:

interface MyInterface2{
    fun foo()
    fun bar(){
        println("bar2")
    }
}

class MyClass : MyInterface,MyInterface2{
    override fun bar() {
        super<MyInterface2>.bar()
    }
    override fun foo() {
        println("foo2")
    }
}

fun main(args:Array<String>){
    var myClass=MyClass()
    myClass.foo()
    myClass.bar()
}

當(dāng)一個(gè)有實(shí)現(xiàn)方法,一個(gè)沒有實(shí)現(xiàn)方法時(shí),默認(rèn)是沒有實(shí)現(xiàn)方法的, super<MyInterface>.foo()切換實(shí)現(xiàn)方法,兩個(gè)都有時(shí)也可以通過這個(gè)方式切換實(shí)現(xiàn)方法

泛型

泛型,即 "參數(shù)化類型",將類型參數(shù)化,可以用在類,接口,方法上。

與 Java 一樣,Kotlin 也提供泛型,為類型安全提供保證,消除類型強(qiáng)轉(zhuǎn)的煩惱。

聲明一個(gè)泛型類:

class Class<T>(t: T) {
    var value = t
}

型變

Kotlin 中沒有通配符類型,它有兩個(gè)其他的東西:聲明處型變(declaration-site variance)與類型投影(type projections)。

聲明處的類型變異使用協(xié)變注解修飾符:in、out,消費(fèi)者 in, 生產(chǎn)者 out。

使用 out 使得一個(gè)類型參數(shù)協(xié)變,協(xié)變類型參數(shù)只能用作輸出,可以作為返回值類型但是無法作為入?yún)⒌念愋停?/p>

in 使得一個(gè)類型參數(shù)逆變,逆變類型參數(shù)只能用作輸入,可以作為入?yún)⒌念愋偷菬o法作為返回值的類型:

class demo<out A>(val a: A) {
    fun foo(): A {
        return a
    }
}

class demo2<in A>(a: A) {
    fun foo(a: A) {
    }
}

泛型約束

我們可以使用泛型約束來設(shè)定一個(gè)給定參數(shù)允許使用的類型。

Kotlin 中使用 : 對(duì)泛型的的類型上限進(jìn)行約束。

fun <T : Comparable<T>> sort(list: List<T>) {
    // ……
}

枚舉類

枚舉類最基本的用法是實(shí)現(xiàn)一個(gè)類型安全的枚舉。

枚舉常量用逗號(hào)分隔,每個(gè)枚舉常量都是一個(gè)對(duì)象。

enum class Color{
    RED,BLACK,BLUE,GREEN,WHITE
}

fun main(args: Array<String>) {
    var color:Color=Color.BLUE

    println(Color.values())
    println(Color.valueOf("RED"))
    println(color.name)
    println(color.ordinal)

}

對(duì)象表達(dá)式和對(duì)象聲明

對(duì)象可以繼承于某個(gè)基類,或者實(shí)現(xiàn)其他接口:

open class A(x: Int) {
    public open val y: Int = x
}

interface B {

}

fun main(args:Array<String>){
    val ab: A = object : A(1), B {
        override val y = 15
    }

    println(ab.y)
}

通過對(duì)象表達(dá)式可以越過類的定義直接得到一個(gè)對(duì)象:

val site = object {
    var name: String = "Jack"
}
    
println(site.name)

匿名對(duì)象可以用作只在本地和私有作用域中聲明的類型。如果你使用匿名對(duì)象作為公有函數(shù)的 返回類型或者用作公有屬性的類型,那么該函數(shù)或?qū)傩缘膶?shí)際類型 會(huì)是匿名對(duì)象聲明的超類型,如果你沒有聲明任何超類型,就會(huì)是 Any。在匿名對(duì)象 中添加的成員將無法訪問。

class A {
    // 私有函數(shù),所以其返回類型是匿名對(duì)象類型
    private fun foo() = object {
        val x: String = "x"
    }

    // 公有函數(shù),所以其返回類型是 Any
    fun Foo2() = object {
        val x: String = "x"
    }

    fun bar() {
        val x1 = foo().x        // 沒問題
        val x2 = publicFoo().x  // 錯(cuò)誤:未能解析的引用“x”
    }
}

對(duì)象聲明

Kotlin 使用 object 關(guān)鍵字來聲明一個(gè)對(duì)象。

object Test{

    fun foo(){
        println("Jack")
    }

}


fun main(args:Array<String>){
    Test.foo()
}

當(dāng)然你也可以定義一個(gè)變量來獲取獲取這個(gè)對(duì)象,當(dāng)時(shí)當(dāng)你定義兩個(gè)不同的變量來獲取這個(gè)對(duì)象時(shí),你會(huì)發(fā)現(xiàn)你并不能得到兩個(gè)不同的變量。也就是說通過這種方式,我們獲得一個(gè)單例。

object My {
    var name: String = "JACK"
}

fun main(args:Array<String>){
    var s1 =  My
    var s2 = My
    s1.name = "HelloJack"
    println(s1.name)
    println(s2.name)

}

伴生對(duì)象

類內(nèi)部的對(duì)象聲明可以用 companion 關(guān)鍵字標(biāo)記,這樣它就與外部類關(guān)聯(lián)在一起,我們就可以直接通過外部類訪問到對(duì)象的內(nèi)部元素。

class MyClass {
    companion object Factory {
        fun create(): MyClass = MyClass()
    }
    fun test(){
        println("Jack")
    }
}

fun main(args:Array<String>){
    val instance = MyClass.create()
    instance.test()
}

我們可以省略掉該對(duì)象的對(duì)象名,然后使用 Companion 替代需要聲明的對(duì)象名:

class MyClass {
    companion object {
    }
}

val x = MyClass.Companion

注意:一個(gè)類里面只能聲明一個(gè)內(nèi)部關(guān)聯(lián)對(duì)象,即關(guān)鍵字 companion 只能使用一次。

類委托

類的委托即一個(gè)類中定義的方法實(shí)際是調(diào)用另一個(gè)類的對(duì)象的方法來實(shí)現(xiàn)的。

// 創(chuàng)建接口
interface Base {
    fun print()
}

// 實(shí)現(xiàn)此接口的被委托的類
class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

// 通過關(guān)鍵字 by 建立委托類
class Derived(b: Base) : Base by b

fun main(args:Array<String>){
    val b = BaseImpl(10)
    Derived(b).print() // 輸出 10
}

lazy() 是一個(gè)函數(shù), 接受一個(gè) Lambda 表達(dá)式作為參數(shù), 返回一個(gè) Lazy <T> 實(shí)例的函數(shù),返回的實(shí)例可以作為實(shí)現(xiàn)延遲屬性的委托: 第一次調(diào)用 get() 會(huì)執(zhí)行已傳遞給 lazy() 的 lamda 表達(dá)式并記錄結(jié)果, 后續(xù)調(diào)用 get() 只是返回記錄的結(jié)果。

val MyLazyValue: String by lazy {
    println("computed!")     // 第一次調(diào)用輸出,第二次調(diào)用不執(zhí)行
    "Hello"
}

fun main(args:Array<String>){
    println(MyLazyValue)
    println(MyLazyValue)
}

一個(gè)常見的用例是在一個(gè)映射(map)里存儲(chǔ)屬性的值。 這經(jīng)常出現(xiàn)在像解析 JSON 或者做其他"動(dòng)態(tài)"事情的應(yīng)用中。 在這種情況下,你可以使用映射實(shí)例自身作為委托來實(shí)現(xiàn)委托屬性。

class Data(val map: Map<String, Any?>) {
    val name: String by map
    val url: String  by map
}

fun main(args:Array<String>){

    val site = Data(mapOf(
            "name" to "Jack1",
            "url"  to "Jack2"
    ))

    // 讀取映射值
    println(site.name)
    println(site.url)

}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容