1. 程式人生 > >Kotlin語法簡記

Kotlin語法簡記

Kotlin語法簡記

一、基礎語法

  1. 包宣告

    Kotlin語言的原始檔,不需要對應匹配檔案目錄和包。

    預設包名default,都會預設匯入kotlin下的相關必須包

    //包名中如果含有,kotlin 的關鍵字,則需要用 反引號 ` 來包裝一下
    package `in`.zhiwei.aqi
  2. 函式定義

    fun關鍵字,格式為args:type,返回型別

    //
    fun sum(a:Int,b:Int):Int{
     return a+b
    }

    若使用lambad類的表示式,返回型別會自動推斷。但:public的函式,必須註明返回型別

    fun sum(a:Int,b:Int) = a+b //此時會自動推斷返回型別
    
    public fun sum(a:Int,b:Int):Int = a+b //public函式,必須註明返回型別

    Unit無返回型別,類似於Java中的Void

    fun printSum(a:Int,b:Int):Unit{
     print(a+b)
    }
    //Unit型別,可以省略,public函式,也可以。

    vararg可變引數

    fun vars(vararg v:Int){
     for(vt in v){
       print(vt)
     }
    }
    //呼叫vars函式
    fun main(args:Array<String>)
    { vars(1,2,3,4,5) }

    lambda匿名函式

    fun main(args:Array<String>){
     val sumLambda:(Int,Int)-> Int = {x,y -> x+y}
     println(sumLambda(1,2))
    }
  3. 常量、變數

    定義變數,常量,使用varval關鍵字,格式var/val name : Type = value,其中Type可以省略,自動推算

    fun main(args:Array<String>){
     var abc:Int = 3
     val PI = 3.1415926
    println(PI) println(abc) }
  4. 註釋

    kotlin註釋,單行 //,多行/* text */且可以巢狀。

  5. 字串模板

    $符號

    • $表示變數名、或變數值
    • $varName表示變數值
    • ${varName.fun()}表示返回值
    var a = 1
    //變數值
    val s1 = "a is $a"
    a = 2
    //返回值
    val s2 = "${s1.replace("is","was")},but now is $a"
  6. NULL檢查機制

    Kotlin的NULL檢查,兩種:

    • !!符號,丟擲異常
    • ?符號,不處理,返回值為null。或者?:三目運算
    //當引用可能為null時候,型別宣告必須標記可為null
    //下面示例,str內容若非整數,則會null,所以返回型別必須宣告可為null
    fun parseInt(str:String):Int?{
     //....
    }
  7. 型別轉換

    使用is!is

    fun getStringLenght(obj:Any):Int?{
     if(obj is String){
       //型別判斷後,會自動轉換
       return obj.length
     }
     // !is表示,不是某個型別
     return null
    }
  8. 區間

    區間表示式,..的rangeTo,配合in!in。注意範圍,數學中的左右閉區間[1,4]

    for (i in 1..4) print(i) // output "1234"
    for (i in 4..1) print(i) // output nothing
    //指定步長
    for (i in 1..4 step 2) print(i)// output "13"
    for (i in 4 downTo 1 step 2) print(i) // output "42"
    //使用until函式,排除結束的那個元素
    for(i in 1 until 10){//[1,10)
     print(i)
    }

二、基礎資料型別

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

- 字面常量

  • 十進位制:123
  • 長整形:123L
  • 16進位制:0x0F
  • 2進位制:0b00001011
  • 注:不支援八進位制
  • Doubles:123.5,123.5e10
  • Floats:123.5f 或123.5F

kotlin
//可以使用下劃線,分隔數字,便宜閱讀
val oneMillion = 1_000_000
val hexBytes = 0xFF_EC_DE_5E

  • 數字比較

    在上面可以看出,Kotlin都是Double、Int、Float等封裝後的型別,沒有基礎的資料型別,避免空指標。

    • ===對比地址
    • ==對比值
    for main(args:Array<String>){
    val a = 100
    print(a===a)//true,自身對比,值與地址皆同
    //包裝
    val boxA:Int? = a
    val boxB:Int? = a
    //對比
    print(boxA == boxB)//true 值相等
    print(boxA === boxB)//false 物件不同,地址不同
    }
  • 型別轉換

    不同於Java的小到大可以自動轉換,Kotlin不可以。

    val b:Byte = 1
    val i:Int = b //這就錯了,b是Byte,不能轉化為Int,他們都是包裝後的
    val l = 1L + 3 //Int和Long這類還可以自動轉
    //其他可以使用toByte,toInt等
  • 位操作符

    適用於Int和Long

    shl(bits) - 左位移(Java中的<<)
    shr(bits) - 右位移(Java中的>>)
    ushr(bits) - 無符號右位移(Java中的>>>)
    and(bits) - &
    or(bits) - |
    xor(bits) - ^
    inv() - reverse
  • 字元

    Kotlin中Char不可與數字直接操作,必須'char'單引號包裹

    c == '1'//不能寫作 c == 1
    //特殊字元可以轉義,或者Unicode表示
  • Boolean

    ||、&&、!

  • Array

    //[1,2,3]
    val arrA = arrayOf(1,2,3)
    val arrB = Array(3,{i -> (i*2)})

    Kotlin中,陣列是不變的型別

  • 字串String

    • 可以"""三對雙引號,來多行
    • for遍歷某個字元
    • trimMargin()來刪除空白
    val text = """
    |第一行
    |第二行
    |第三行
    """
    //預設使用`|`作為邊界字首,也可以自定義
    trimMargin(">")

三、條件控制

  • if表示式

    var max = a
    if(a<b){
    max = b
    }else{
    max = a
    }
    //或者,類似Java中的三目運算子
    var max = if(a<b) a else b
  • 使用區間

    //x..y
    var a = 7
    if(x in 1..9) print("a in this range")
  • when

    類似其他程式語言中的Swith...case

    when(x){
    1-> print("x == 1")
    2-> print("x == 2")
    3,4-> print("x == 3 or 4")
    in 5..9-> print("x is in this range")//還有!inis ,!is
    in validNumbers-> print("x is valid")
    else-> print("x not 1 or 2")//類似defalut
    }

四、迴圈控制

  • for迴圈

    for(item in collection) print(item)
    for(i in array.indices) print(array[i])
    for((index,value) in array.withIndex()) {
    print("item $index is $value")
    }
  • while,do…while

  • return、break、continue

    //Break和Continue 可以配合label標籤,確定其作用範圍,格式[email protected]
    [email protected] for(i in 1..100){
    for(j in 1..100){
      if(...) break@loop
    }
    }
    //return 返回,也可以用label標籤確定範圍
    fun foo(){
    ints.forEach{
     if(it == 0)return//這麼return,就return了真個foo函式
     print(it)
    }
    }
    //配合lable
    fun foo(){
    ints.forEach [email protected]{
      if(it == 0)return@lit//如此,只return出forEach,
      print(it)
    }
    }
    //可以不用顯示宣告label,
    fun foo(){
    ints.forEach{
      if(it == 0)return@forEach//隱式label的使用
      print(it)
    }
    }
    //也可以,將lambad中宣告匿名函式,return就會在它的範圍內return
    fun foo(){
    ints.forEach(fun(value:Int)){
      if(it==0)return
      print(it)
    }
    }
    //return帶有返回值的時候,帶有label的return,優先
    return@abc 1//從標籤abc那裡,返回出去數值 1

五、類和物件

//定義類
class Demo{
  var name:String = "Joe"
  var url:String = "www.zhiwei.in"
  val PI = 3.1415926
  //成員函式
  fun foo(){
    print("Foo")
  }
}
//空類
class Empty
//例項化
val demo = Demo()//kotlin 中沒有new
demo.name
//建構函式,一個主的,可有多個次的,主建構函式,就在class標題處
class Person constructor(firstName:String){
  //...
}
//若主建構函式,方法體為空,則constructor可以省略
class Person(firstName:String){}

//geter setter 可選
var lastName:String = "Green"
    get()=field.toUpperCase()
    set
var no:Int = 100
    get() = field
    set(value){
    ...
    }
var height:Float = 175.0f
    private set
//field關鍵字,用於後端變數,kotlin中不能有欄位
//非空屬性必須定義時候就初始化,kotlin使用lateinit來延遲初始化
public class MyTest{
  lateinit var subject:TestSubject

  @SetUp fun setup(){
    subject = TestSubject()
  }
  @Test fun test(){
    subject.method()
  }
}
  • 主構造器

    不能包含任何程式碼,需要在init中初始化

class Person constructor(firstName:String){
  init{
    //...
  }
}
//類的引數,可以在建構函式中
class People(val firstName:String, val lastName:String){
  //...
}
  • 次建構函式

    class Person{
    //如果沒有主建構函式,則每個次建構函式,可能需要依次呼叫代理
    //類似與Java中的多個建構函式,內部依次呼叫
    constructor(parent:Person){
      parent.children.add(this)
    }
    }
    //沒有顯式的宣告建構函式的話,會有一個預設的public的建構函式,類似於Java
  • 抽象類

    abstract關鍵字

    //可繼承
    open class Base{
    open fun f(){}
    }
    abstract class Sub:Base{
    override abstract fun f()
    }
  • 巢狀類

    class Outer{
    private val bar:Int = 1
    class Nested{
      fun foo() = 2
    }
    }
    //呼叫巢狀
    fun main(args:Array<String>){
    val demo = Outer.Nested().foo()//呼叫格式,Outer.Nested().foo()
    println(demo)
    }
  • 內部類

    inner關鍵字

    //內部類會擁有外部類的引用,不同於巢狀類
    class Outer{
    private val bar:Int = 1
    var v = "member property"
    inner class Inner{
      fun foo() = bar
      fun innerTest(){
        var o = this@Outer
        println("持有外部引用:"+o.v)
      }
    }
    }
    //呼叫
    fun main(args:Array<String>){
    val demo = Outer().Inner().foo()//格式,與巢狀類,略有不同
    println(demo)
    }
  • 匿名內部類

    class Test{
    var v = "abc"
    fun setInterFace(test:TestInterFace){
      test.test()
    }
    }
    //interface
    interface TestInterFace{
    fun test()
    }
    //呼叫
    fun main(args:Array<String>){
    var test = Test()
    //匿名內部類
    test.setInterFace(obj:TestInterFace{
      override fun test(){
        //...
      }
    })
    }
  • 類的修飾符

    • 屬性修飾符
symbol description
abstract 抽象
final 不可繼承,kotlin的類,預設都是final
enum 列舉
open 可繼承
annotation 註解類

- 許可權修飾符

symbol description
private 私有,同一檔案內可見
protect 同一檔案內,或子類 可見
public 公開
internal 同一模組中可見

六、繼承

kotlin中所有類的繼承,都是Any類,類似於Java中的Object

//Any類中含有以下三個函式
equals()
  hashCode()
  toString()
//open宣告可被繼承
open class Base(p:Int)
class Sub(p:Int):Base(p:Int)
  • 繼承的建構函式
class Student:Person{
  //子類沒有主建構函式,則需要在次建構函式中,super父類的建構函式
  constructor(ctx:Context):super(ctx){}
}
//若是子類有主建構函式,基類也應一致,且在子類繼承時候,初始化
open class Person(var name:String,var age:Int){}
//此處,:Person被初始化了name,age
class Student(name:String,age:Int):Person(name,age){
  //...
}
  • 重寫函式,屬性

基類函式,預設final,需要open的方可重寫,重寫用override標記

若繼承的基類,或介面中,多個基類或介面含有相同函式,則需要複寫,且顯示super.fun() 呼叫

屬性的重寫

open class Foo{
  open val x:Int get{...}
}
class Bar:Foo(){
  override val x : Int = ...
}
//屬性的重寫,需要相容,var可以重寫val,但是val不能重寫var
//可以在建構函式中直接宣告 重寫
interface Foo{
  val count:Int
}
class Bar1(override val count:Int):Foo
class Bar2:Foo{
  override var count:Int = 0
}

七、介面

類似Java8,interface關鍵字,可有預設實現

interface MyInterface{
  fun bar()
  fun foo(){
    println("foo")
  }
}
  • 一個類可以實現多個介面

  • 介面的屬性,抽象無值,必須複寫

    interface MyInterface{
    var name:String
    }
    class MyImpl :MyInterface{
    override var name:String = "Kotlin"
    }

八、擴充套件

Kotlin支援對類的靜態擴充套件,不需要繼承,或者裝飾器模式設計

  • 擴充套件函式
//格式,擴充套件物件、擴充套件函式、引數(Nullable)
fun receiverType.functionName(params){
  body
}
//示例:
class User(var name:String)
//新增一個print函式給User類
fun User.Print(){
  print("name:$name")
}
//呼叫
fun main(args:Array<String>){
  var user = User("kotlin")
  user.Print()//擴充套件新增的函式
}

靜態擴充套件的,擴充套件到那個類,就是屬於那個類/介面,而不會是它的實現/子類。

  • 屬性也可被擴充套件,但不能初始化。

  • 類內部擁有的其他物件,也可以被擴充套件

    class MyClass{
    companion object{}//Companion的物件
    }
    fun MyClass.Companion.foo(){
    print("擴充套件了類的擁有的物件")
    }
  • 擴充套件作用域

    package foo.bar
    fun Baz.goo(){...}
    //其他包使用
    package com.example.usage
    //import 匯入
    import foo.bar.goo
    fun usage(baz:Baz){
    //就能原本不能訪問的
    baz.goo()
    }
  • 擴充套件成員,分發接收者、擴充套件接收者

    class D{
    fun duck() print("Dark duck")
    }
    class C {
    fun cook()print("Cook food")
    //擴充套件D的函式
    fun D.ext(){
      duck()//能夠呼叫D的函式
      cook()//也能呼叫C的函式
    }
    //提供呼叫擴充套件函式的方法
    fun callDext(d:D){
      d.ext()//呼叫擴充套件函式
    }
    }
    //示例
    fun main(args:Array<String>){
    val c:C = C()//c被稱為分發接收者
    val d:D = D()//d被稱為擴充套件接收者
    c.callDext(d)
    }

九、資料類&密封類

kotlin可建立一個只包含資料的類,data關鍵字

  • 資料類主建構函式,至少一個引數
  • 引數標識val或var
  • 資料類不能abstract、open、sealed或inner
  • 不能繼承其他基類,可以多介面實現

附帶一些函式equals/hashCode;toString,copy

  • 複製:

    data class User(val name:String, val age:Int)
    
    fun main(args:Array<String>){
    val jack = User(name="Jack",age = 1)
    val olderJack = jack.copy(age = 2)
    print(jack)
    print(olderJack)
    }
    //解構宣告
    val jane = User("Jane",27)
    val(name,age) = jane
    println("$name,$age years of age")
    //輸出結果:Jane,27 years of age

    標準資料類PairTriple,但是建議自定義資料類使用

  • 密封類

    sealed關鍵字,用於限定類中的值的型別,而不是任意型別

    密封類可有子類,但必須在其中內嵌

    sealed不能修飾interface、abstract類,會警告,但不會編譯錯誤

sealed class Expr//密封類
data class Const(val number:Double):Expr()//密封類的子類
data class Sum(val e1:Expr,val e2:Expr):Expr()//密封類的子類
object NotANumber:Expr()

fun eval(expr :Expr):Double = when(expr){
  is Const -> expr.number
  is Sum -> eval(expr.e1)+eval(expr.e2)
  NotANumber -> Double.Nan
}

十、泛型

class Box<T>(t:T){
  var value = t
}
//示例,例項化時候,需要具體引數型別
val box:Box<Int> = Box<Int>(1)
//或
val box = Box(1)

//函式
fun <T> boxIn(value:T) = Box(value)
val box2 = boxIn<Int>(1)
val box3 = boxIn(1)//自動推測型別

//可推斷的型別,可不申明
fun main(args: Array<String>) {
    val age = 23
    val name = "runoob"
    val bool = true

    doPrintln(age)    // 整型
    doPrintln(name)   // 字串
    doPrintln(bool)   // 布林型
}

fun <T> doPrintln(content: T) {
//when內做了型別判斷
    when (content) {
        is Int -> println("整型數字為 $content")
        is String -> println("字串轉換為大寫:${content.toUpperCase()}")
        else -> println("T 不是整型,也不是字串")
    }
}

泛型約束,類似於Java中的T extends ABC之類的

fun <T : Comparable<T>> sort(list:List<T>){
  //...
}
//多個上限約束,使用where
fun <T> cloneWhenGreater(list:List<T>,threshold:T):List<T> where T:Comparale,Cloneable{
  //...
}
  • 型變

    kotlin沒有萬用字元,有declaration-site variancetype projections

    • in 消費,入參,非出參
    • out 生產,作為出參,而不能入參(即,返回型別,而非傳入型別)
    //定義支援協變的類
    class Test<out A>(val a:A){
    fun foo():A{
      return a
    }
    }
    //
    fun main(args:Array<String>{
    var strCo:Test<String> = Test("a")
    var anyCo:Test<Any> = Test<Any>("b")
    anyCo = strCo
    println(anyCo.foo())//輸出結果為 “a”
    }
    //定義一個支援逆變的類
    class Test<in A>(a:A){
    fun foo(a:A){
      //...
    }
    }
    //
    fun main(args:Array<String>){
    var strDCo = Test("a")
    var anyDCo = Test<Any>("b")
    strDCo = anyDCo
    }
  • 星號投射

    Function<*,Sting>,表示Function<in Nothing,String>
    Function<Int,*> ,表示Function<Int,out Any?>
    Function< , > ,表示Function<in Nothing,out Any?>

十一、列舉類

enum class Color{
  RED,BLACK,BLUE,GREEN,YEELOW,WHITE
}
//或
enum class Color(val rgb:Int){
  RED(0xFF0000),
  GREEN(0x00FF00)
}
//匿名內部類形式
enum class ProtocolState{
  WAITING{
    override fun signal() = TALKING
  },
  TALKING{
    override fun signal()= WAITING
  };
  abstract fun signal():ProtocolState
}

列舉中,成員用;分隔

enum class Color{
  RED,BLACK,BLUE,GREEN,WHITE
}
//test
fun main(args: Array<String>) {
    var color:Color=Color.BLUE

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

十二、物件表示式&宣告

//匿名類物件
window.addMouseListener(obj:MouseAdapter(){
  override fun mouseClicked(e:MouseEvent){
    ...
  }
})
//或繼承基類,介面的物件
val ab:A = obj:A(1),B{//A 是基類,B是介面,ab宣告一個A型別物件,= 一個A的子類且實現了B介面的物件
  override val y = 15
}
//越過類的定義
fun main(args: Array<String>) {
    val site = object {
        var name: String = "菜鳥教程"
        var url: String = "www.runoob.com"
    }
    println(site.name)
    println(site.url)
}
//建構函式,共有的話,則public,
class C {
    // 私有函式,所以其返回型別是匿名物件型別
    private fun foo() = object {
        val x: String = "x"
    }

    // 公有函式,所以其返回型別是 Any
    fun publicFoo() = object {
        val x: String = "x"
    }

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

object關鍵字,宣告物件

object Site {
    var url:String = ""
    val name: String = "Google Cloud"
}
fun main(args: Array<String>) {
    var s1 =  Site
    var s2 = Site
    s1.url = "www.google.com"
    println(s1.url)
    println(s2.url)
}
//兩個輸出,都是同一個url
class Site {
    var name = "google cloud"
    object DeskTop{
        var url = "www.google.com"
        fun showName(){
            print{"desk legs $name"} // 錯誤,不能訪問到外部類的方法和變數
        }
    }
}
fun main(args: Array<String>) {
    var site = Site()
    site.DeskTop.url // 錯誤,不能通過外部類的例項訪問到該物件
    Site.DeskTop.url // 正確
}

類內部的物件宣告,可以用companion關鍵字標記,構成伴生,一個類只有一個伴生物件,即companion只能出現一次

class MyClass{
  companion object Factory{
    fun create():MyClas = MyClass()
  }
}
//access
val instance = MyClass.create()

十三、委託

kotlin直接支援委託模式,即一個類中的方法,呼叫另一個類的函式實現by關鍵字

  • 類委託
// 建立介面
interface Base {   
    fun print()
}

// 實現此介面的被委託的類
class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

// 通過關鍵字 by 建立委託類,由Base的實現類,具體操作
class Derived(b: Base) : Base by b

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

    某個屬性,是由其他類代理實現

    var/val name:Type by 表示式

    • 定義為拖類,包含getValue、setValue函式,thisRef、prop
    // 定義包含屬性委託的類
    class Example {
      var p: String by Delegate()
    }
    
    // 委託的類
    class Delegate {
      operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
          return "$thisRef, 這裡委託了 ${property.name} 屬性"
      }
    
      operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
          println("$thisRef 的 ${property.name} 屬性賦值為 $value")
      }
    }
    fun main(args: Array<String>) {
      val e = Example()
      println(e.p)     // 訪問該屬性,呼叫 getValue() 函式
    
      e.p = "google kotlin"   // 呼叫 setValue() 函式
      println(e.p)
    }
  • 標準委託

    • 延遲屬性Lazy
    val lazyValue: String by lazy {
        println("computed!")     // 第一次呼叫輸出,第二次呼叫不執行
        "Hello"
    }
    
    fun main(args: Array<String>) {
        println(lazyValue)   // 第一次執行,執行兩次輸出表達式
        println(lazyValue)   // 第二次執行,只輸出返回值
    }
    • Observable
    class User {
        var name: String by Delegates.observable("初始值") {
            prop, old, new ->
            println("舊值:$old -> 新值:$new")
        }
    }
    
    fun main(args: Array<String>) {
        val user = User()
        user.name = "第一次賦值"
        user.name = "第二次賦值"
    }
    //執行結果
    舊值:初始值 -> 新值:第一次賦值
    舊值:第一次賦值 -> 新值:第二次賦值
    • map 對映
    class Site(val map: Map<String, Any?>) {
        val name: String by map
        val url: String  by map
    }
    
    fun main(args: Array<String>) {
        // 建構函式接受一個對映引數
        val site = Site(mapOf(
            "name" to "Google Cloud",
            "url"  to "www.google.com"
        ))
    
        // 讀取對映值
        println(site.name)
        println(site.url)
    }
    //結果
    Google Cloud
    www.google.com
    • Not Null
    class Foo {
        var notNullBar: String by Delegates.notNull<String>()
    }
    //若是不賦值,就print訪問,則會拋異常
    foo.notNullBar = "bar"
    println(foo.notNullBar)
    • 區域性委託屬性

尷尬,粗略的看一遍,真的有點累,以後慢慢充實….