Kotlin零基礎入門到精通

一. Kotlin課程概述

1.1 課程安排:

  1. 課程介紹、Kotlin介紹、開發環境搭建
  2. 基本語法:基本類型、空安全類型、智能轉換、類與對象同步、數組與區間
  3. 程序結構:常量與變量、函數、Lambda、類成員、條件表達式、循環語句、運算符、異常捕獲
  4. 面向對象:抽象類和接口、Object、伴生對象、擴展方法、屬性代理、數據類、內部類、枚舉與密封類
  5. 高階函數:基本概念、常見高階函數、尾遞歸優化、閉包、函數複合、科裏化、偏函數
  6. DSL:基本概念、案例開發、Gradle腳本
  7. 協程:基本概念、協程的使用、封裝協程庫、協程原理分析;
  8. 與Java混編:基本互操作、正則表達式、集合框架、IO操作、裝箱與拆箱、NoArg插件、AllOpen插件、註解處理器
  9. 應用與展望:前景與展望、編寫腳本、服務端、前端、Android、Native

1.2 什麼是Kotlin?

  • Kotlin就是一門可以運行在Java虛擬機、Android、瀏覽器上的靜態語言,它與Java 100%兼容,如果你對Java非常熟悉,那麼你就會發現Kotlin除了自己的標準庫之外,大多仍然使用經典的Java集合框架;
  • 總結來說:
    1. Android官方開發語言
    2. 100%兼容Java
    3. Kotlin-Js 前端開發
    4. Kotlin-Jvm 服務端開發
    5. Kotlin-Native 本地執行程序

    Kotlin 是一門全棧語言

1.3 Kotlin的發展歷程

  • 2010年立項
  • 2011.6對外公開
  • 2012.2開源
  • 2013.8 支持 Android Studio
  • 2014.6全新的開源web站點和域名 Kotlinlang.org
  • 2016.2 發佈1.0
  • 2016.9 發佈1.0.4, 支持apt

1.4 學習目標

  • 學會使用Kotlin
  • 熟悉Java生態
  • 瞭解一些特性的背後實現

1.5 必備知識

  • 熟悉計算機基礎、操作系統相關的知識
  • 瞭解Java及其生態
  • 瞭解Java工程組織的常用工具
  • 熟悉IntelliJ Idea

1.6 參考資料

  • 官方文檔:https://kotlinlang.org/docs/reference
  • Kotlin源碼:https://github.com/JetBrains/kotlin
  • Kotlin官博:https://blog.jetbrains.com/kotlin/
  • Kotlin微信公衆號:Kotlin

1.7 Hello,world

  1. 安裝Kotlin插件,如圖所示:
    在這裏插入圖片描述

    安裝後要重啓一次才能生效;

  2. 創建一個Kotlin工程,如圖所示:
    在這裏插入圖片描述
    3. 根據實際,填寫項目的groupId,ArtifactId,以及Version,如圖所示:
    在這裏插入圖片描述

  3. 一路默認下去即可,然後成功創建項目,然後在此項目中創建Kotlin包,如圖所示:
    在這裏插入圖片描述

  4. 緊接着創建包net.println.kotlin(根據實際來,此處可不一致),然後創建HelloWorld.kt,如圖所示:
    在這裏插入圖片描述

  5. 編寫Hello,world代碼如下:

    fun main(args:Array<String>){
    	println("Hello World")
    }	
    
  6. 執行,如圖所示:
    在這裏插入圖片描述

    執行成功後,控制檯會打印Hello,world字樣,說明運行成功,如圖所示: 在這裏插入圖片描述

  7. 點擊println進去,可以看到源碼,其打印操作是調用的Java的System.out.println,如圖所示:
    在這裏插入圖片描述

二. 數據類型

2.1 本章目標

  • 認識基本類型
  • 初步認識類及其相關概念
  • 認識區間和數組

簡單來說就是看懂如圖例子:
在這裏插入圖片描述

2.2 Boolean類型

  • Boolean 值 只有true或者false兩個值,它無處不在,相當於Java類型的boolean
  • 示例代碼:
    val aBoolean : Boolean = true
    val anotherBoolean : Boolean = false
    
    > var是可變變量(可讀可寫),val是可讀變量(只能讀);同時 : 後面的Boolean 是指它的類型,前面的aBoolean或anotherBoolean是它的變量名稱,=號後面的true或false是它的值;

2.3 Number類型

  • 數字類型如下:
    在這裏插入圖片描述

  • int,Long類型的最大值和最小值

    // 2147483647
    val maxInt: Int= Int.MAX_VALUE
    // -2147483648
    val mintInt: Int=Int.MIN_VALUE
    val maxLong : Long=Long.MAX_VALUE
    val minLong: Long=Long.MIN_VALUE
    val aFolat: Float=20F
    val maxFolat: Float=Float.MAX_VALUE
    val minFolat: Float=- Float.MAX_VALUE
    val maxShort : Short=Short.MAX_VALUE
    val minShort : Short=Short.MIN_VALUE
    val maxByte: Byte= Byte.MAX_VALUE
    val minByte : Byte= Byte.MIN_VALUE
    

    val後面的爲變量名,可替換爲實際名稱;Long類型的長整形後面可以加個L; Float類型後面必須加F,f;Float是浮點類型,有精度問題,計算錢相關的不要用這個;

  • 裝箱和拆箱

    • 在Kotlin中不區分裝箱和拆箱;

2.4 Char類型

  • 特點:

    • 字符對應Java的Character
    • 佔兩個字節,表示一個16位的Unicode字符
    • 字符用單引號’‘引起來,例如: ‘a’,‘0’,’\n’
  • Char類型轉義字符如圖:
    在這裏插入圖片描述

  • 不可隱式轉換

    • 在java中,一個int類型與Long相加,原本的Int類型會自動隱式轉換爲Long類型,這在Kotlin中是不允許的;
  • 比較相等

    • ==表示equals 值得相等比較
    • === 三個等號表示引用地址的比較,即比較兩個值是否是同一個引用地址;
  • 字符串模板

    • 代碼如圖:
      val arg1: Int =0
      val arg2: Int =1
      // java款式的加法
      println(""+arg1+"+"arg2+"="+(arg1+arg2))
      // kotlin款式的加法
      println("$arg1+ $arg2=${arg1+arg2}")
      // 如果要打印美元 $這個符號,則再加一個$
      val salary: Int = 1000
      println("$"+"$salary")
      // 或者使用轉義符號
      println("\$salary")
      // 三個引號,轉義會失效,支持換行
      val txt:String ="""
      hello
      ,
      world
      """
      // 打印它的字符數量
      println(rawString.length)
      

2.5 類與對象

  • 什麼是類?

    • 類,一個抽象的概念
    • 具有某些特徵的事物的概括
    • 不特定指代任何一個具體的事物
    • 舉例:
      • 人、車、書
    • 寫法:
      class<類名>{<成員>}
      
  • 什麼是對象?

    • 是一個具體的概念,與類相對
    • 描述某一種類的具體個體
    • 舉例:
      • 某些人、領導的車、你手裏的那本書
  • 類和對象的關係?

    • 一個類通常可以有很多個具體的對象
    • 一個對象本質上只能從屬於一個類
    • 某一個人,他是工程師,但本質上還是屬於人這一類
  • 對象也經常被稱作“類的對象”或者“類的實例”

    • 比如 類: 城市 --> 上海、深圳(對象)
  • 類的繼承

    • 提取多個類的共性得到一個更抽象的類,即父類;
    • 子類擁有父類的一切特徵
    • 子類也可以自定義自己的特徵
    • 所有類都最終繼承自Any
  • 類與對象的實例圖示:
    在這裏插入圖片描述

  • 如果在類中加了init字段,則在創建過程中會自動執行其中的方法,如圖所示:
    在這裏插入圖片描述

  • 當構造只有一個的時候可以進行省略,如果有多個則不可以,如圖所示:
    在這裏插入圖片描述

  • 子類繼承了父類中的一些方法,如圖所示:
    在這裏插入圖片描述

  • Any是一切類的父類,它擁有equals,hashCode,toString方法,則說明在Kotlin中的其他所有類,都擁有這些方法,如圖所示:
    在這裏插入圖片描述

2.6 空類型和智能類型轉換

  • 任意類型都有可空和不可空兩種
    • val notNull:String = null // 錯誤,不能爲空。 如果爲空會拋出異常
    • val nullable:String ?= null // 正確,可以爲空。 如果爲空,則被賦值的nullable的值爲null
    • notNull.length // 正確,不爲空的值可以直接使用
    • nullable.length // 錯誤,可能爲空,不能直接獲取長度
    • nullable!!.length // 正確,強制認定nullable不可空(如果在這段代碼前進行了if判斷,比如不爲空的時候才執行的這段代碼,就沒有問題。我們已經認定了這個nullable變量不爲空)
    • nullable?.length // 正確,若nullable爲空,返回空
  • Java Style類型轉換
    • val sub: SubClass = parent as SubClass
    • 類似於Java 的類型漢族那換,失敗則拋出異常;

    這裏的含義是,判斷parent變量爲SubClass的子類,若爲其子類,則sub是SubClass類型,如不爲則直接拋出異常;

- 安全類型轉換
val sub: SubClass? = parent as? SubClass
- 如果轉換失敗,返回null,不拋異常
> 如果parent不是SubClass的子類,則類型轉換失敗,sub不能編程SubClass類型,則sub的值爲null;

2.7 包

  • 概述:
    • 包就是命名空間
    • 包的聲明必須在非註釋代碼的第一行
    • 類的全名:
      • net.println.kotlin,chapter2.HelloWorld
  • 包即類的全名,import字段進行導入,import同時可以對此包進行命名,可以以另外一個名稱進行替代和調用。如圖所示:
    package net.println.koltin.DemoTest
    import net.println.kotlin.HelloWord as Hello
    
    fun main(args:Array<String>){
    	val sayHello:Hello=Hello();
    }
    

    這裏導入的Hello,就是HelloWorld類。通過創建Hello,就相當於創建了一個HelloWord,所以sayHello看似是對Hello實例化,實際上是對HelloWorld進行了實例化;

2.8 區間

  • 概述:
    • 一個數學上的概念,表示範圍
    • ClosedRange的子類,IntRange最常用
  • 基本寫法:
    • 0…100表示[0,100]
    • 0 until 100 表示 [0,100)
    • i in 0…100判斷 i 是否在區間 [0,100]中
    • 示例代碼:
      // 前後都閉區間  [0,1024]
      val range:IntRange=0..1024
      // 前閉後開的區間  [0,1024) = [0,1023]
      val range_exclusive:IntRange= 0 until 1024  
      val empty_Range:IntRange=0..-1
      
      fun main(args:Array<String>){
      	// true   是爲空,因爲此範圍中沒有值
      	println(emptyRange.isEmpty())
      	// true 此範圍中包含了50
      	println(range.contains(50))
      	// true 檢查50是否在range的範圍中
      	println(50 in range)
      }
      
      // 下面打印出來的結果爲: 0,1,2,3,4,5,6,7,8,9,10...
      for(i in range_exclusive){
      	println("$i,")
      }
      

      用i in IntRange可以作範圍判斷,以及輔助遍歷等操作

2.9 數組

  • 數組是什麼?

    • 對應英文單詞Array:
      • An impressive display or range of a particular type of thing or an ordered arrangement,in particular
    • 跟數一點關係沒有
    • 就是一系列對象,這個對象可以是各類型的數字,字符,字符串,或者自定義對象等;
  • 基本寫法

    • val array:Array arrayOf(…)
  • 基本操作:

    • println array[i] 輸出第i個成員
    • array[i] 指定數組的第i個成員值;我們可以通過此進行賦值或者獲取值
    • array.length 數組的長度
  • 爲了避免不必要的裝箱和拆箱,基本類型的數組是定製的,如圖所示:
    在這裏插入圖片描述

  • 數組示例代碼:

    • 創建對象,如圖所示:
      在這裏插入圖片描述
    • 創建示例代碼如下:
      // 創建一個int數組
      val arrayOfint: IntArray = intArrayOf(1,3,5,7)
      // 創建一個Char數組
      val arrayOfChar: CharArray = charArrayOf("H","e","l","o","W","o","r","l","d")
      // 創建一個自定義對象數組,根據上面創建的類
      val arrayOf書記: Array<市委書記> = arrayOf(市委書記("張"),市委書記("趙"),市委書記("黃"))
      
      fun main(args: Array<String>){
      	// 打印 int數組
      	println(arrayOfInt.size)
      	for(int in arrayOfInt){
      		println(int)
      	}
      }
      
      println(arrayOf書記[1])
      // 這裏將方書記賦值給了數組的1 索引處。此處1索引原來的對象會變成新替換的對象,所以重新打印1索引處的書記會變成新的方書記;
      arrayOf書記[1] = 市委書記("方")
      println(arrayOf書記[1])
      
      // 這裏的joinToString("")表示每個字符之間不用什麼連接。 傳入制定參數就以指定值進行連接。如果不傳,默認以,號連接,比如: H,e,l,l,o...    此處代碼打印結果爲: HelloWorld
      println(arrayOfChar.joinToString(""))
      
      

三. 程序結構

3.1 常量與變量

  • 什麼是常量?

    • val= value, 值類型
    • 類似Java 的final
    • 不可能重複複製;
    • 舉例:
      • 運行時常量: val x=getX()
      • 編譯期常量(Java中的靜態常量 final): const val x=2
  • 什麼是變量:

    • var = variable
    • 舉例:
      • var x =“HelloWorld” //定義變量
      • x= “HiWorld” // 再次賦值
  • 類型推導- 編譯器可以推導量的類型

    • val String =“Hello” // 推導出String類型
    • val int =5 // Int類型
    • var x =getString() + 5 // String類型
  • var是可變量,val 是不可變,它是常量;

  • val雖然是不可變,但是它不是靜態的,如果需要在編譯期時就加在,可以在前面加一個const字段;

3.2 函數

  • 什麼是函數?
    • 以特定功能組織起來的代碼塊
    • 舉例:
      • fun sayHi(name:String){println(“Hi,$name”)}
        // 簡寫的方式:
      • fun sayHi(name:String)=println(“Hi,$name”)
  • 任何函數都是以fun 開頭,然後後面爲它的名字,括號內爲它的參數若有範圍值,在後面用:Any ,Any代表返回值類型;
  • 比如我們的Main函數:
    fun main(args: Array<String>){
    	println("Hello,world")
    	// 打印args數組的索引爲1 的值
    	println(${args[0]})
    	if (args.size !=2){
    		
    	}
    }
    

    如果沒有傳參,會報數組索引異常。這裏main函數沒有爲args賦值;如果啓動時,傳入了參數就可以打印了;同時如果有返回值,則需要在main(…)這個括號後面加 :返回值類型來定義返回值類型;

  • 代碼示例:
    fun main(args: Array<String>){
    	checkArgs(args)
    	val arg1=args[0].toInt()
    	val arg2=args[1].toInt()
    	println("$arg1+$arg2=${sum(arg1,arg2)}")
    }
    
    fun checkArgs(args: Array<String>){
    	if(args.size != 2){
    		println("請傳入兩個整形參數,例如 1, 2")
    		System.exit(-1)
    	}
    }
    
    fun sum(arg1: Int ,arg2:Int):Int{
    	return arg1+arg2
    }
    

3.3 Lambda表達式

  • 什麼是lambda表達式?

    • 匿名函數
    • 寫法:
      {[參數列表]->[函數體,最後一行是返回值]}
      
    • 舉例:
      val sum ={a:Int,b:Int-> a+b}
      
  • Lambda的類型表示舉例:

    // 無參,返回值爲Unit
    ()->Unit
    // 傳入整型,返回一個整型
    (Int)->Int
    // 傳入字符串、Lambda表達式,返回Boolean
    (String,(String)->String)->Boolean
    
  • Lambda表達式的調用

    • 用()進行調用
    • 等價於invoke()
    • 舉例:
      val sum ={a:Int,b:Int->a+b}
      sum(2,3)
      sum.invoke(2,3)
      
  • Lambda表達式的簡化

    • 函數參數調用時最後一個Lambda可以移出去
    • 函數參數只有一個Lambda,調用時小括號可省略
    • Lambda只有一個參數可默認爲it
    • 入參,返回值與形參一致的函數可以用函數引用的方式作爲實參傳入;
  • 使用for 我們用foreach也可以進行遍歷;

3.4 類成員

  • 什麼是類成員?

    • 屬性:或者說成員變量,類範圍內的變量
    • 方法:或者說成員函數,類範圍內的函數
  • 函數和方法的區別?

    • 函數強調功能本身,不考慮從屬
    • 方法的稱呼通常是從類的角度出發
    • 叫法不同而已,不要糾結;
  • 函數如何定義方法?

    • 寫法與普通函數一致,函數如果寫在類中,它就是方法
    • 舉例:
      class Hello{
      	fun sayHello(name:String)=println("Hello,$name")
      }
      
  • 定義屬性

    • 構造方法參數中val/var的都是屬性
    • 類內部也可以定義屬性
    • 舉例:
      class Hello(val aField:Int,notAField:Int){
      	val anotherFIeld:Float=3f
      }
      
  • 屬性的訪問規則:

    • 屬性可以定義getter/setter
    • 舉例如下:
      val a:Int=0
      get()=field
      var b:Float=0f
      set(value){field=value}
      

      get(){return field} 其中field指代了此變量

  • 屬性初始化

    • 屬性的初始化儘量在構造方法中完成
    • 無法在構造方法中初始化,嘗試降級爲局部變量
    • val用lateinit延遲初始化,val用lazy
    • 可空類型謹慎用null直接初始化;
    • 舉例:
      class X
      class A{
      // 使用lateinit延遲初始化,不需要立即給值
      lateinit var c: String
      val e: X by lazy{
      	X()			
      	}

3.5 運算符

  • 任何類可以定義或者重載父類的基本運算符
  • 通過運算符對應的具名函數來定義
  • 對參數個數作要求,對參數和返回值類型不作要求
  • 不能像Scala一樣定義任意運算符

3.6 表達式

  • 中綴表達式

    • 只有一個參數,且用infix修飾的函數
    • 舉例:
      class Book{
      	infix fun on(place: String){
      		...
      	}
      }
      if(Book() on "My Desk"){...}
      
      
  • if 表達式

    • 舉例:
      if(a==b)
      	...
      else
      	...
      

      在if表達式中,我們既可以用來做條件判斷,也可以類似java三元運算符一樣直接用來當一個值進行使用。比如

      val b=2
      // 當b等於2時,則a等於3  否則等於5
      val a= if(b==2) 3 else 5
      
    • 表達式完備性:
      • 當我們用於類似三元運算符的操作時,必須要有else
  • When 表達式

    • 加強版Switch,支持任意類型
    • 支持純表達式條件分支(類似if)
    • 表達式與完備性
    • 舉例:
      fun main(args: Array<String>){
      	val x=5
      	when(x){
      		is Int-> 邏輯代碼...
      		in 1..100-> 邏輯代碼...
      		!in 1..100-> 邏輯代碼...
      		args[0].toInt()-> 邏輯代碼...
      	}
      }
      

      這裏的when處傳入值,下面任意滿足條件且按先後只執行第一個符合條件的代碼邏輯;

3.7 循環

  • for循環

    • 基本寫法
      for(element in elements)...
      
    • 代碼示例:
      fun main(args: Array<String>){
      	// 遍歷一
      	for(arg in args){
      		println(arg)
      	}
      	// 遍歷二
      	for((index,value) in args.withIndex()){
      		println("$index-> $value")
      	}
      	// 遍歷三
      	for(indexedValue in args.withIndex()){
      		println("${indexedValue.index} -> ${indexedValue.value}")
      	}
      }
      
    • 給任意類實現Iterator方法

      可網上找

  • While循環

    • 古董級語法
    • do … while(…)…
    • while(…)…
    • 代碼示例:
      fun main(args:Array<String>){
      	var x=5
      	while(x>0){
      		println(x)
      		x--
      	}
      	
      	do {
      		println(x)
      		x--
      	}while(x>0)
      }
      
  • 跳過和終止循環

    • 跳過當前循環用continue
    • 終止循環用break
    • 多層循環嵌套的終止結合標籤使用

3.8 捕獲異常

  • catch 分支匹配異常類型
  • 表達式,可以用來賦值
  • finally: 無論代碼是否拋出異常都會執行
  • 注意下面的寫法:
    return try(
    	x/y
    	)catch(e:Exception){
    	0
    	}finally{
    		...
    	}
    

    異常的捕獲及處理與java類似,不過其可以作爲值進行使用;

3.9 各種類型參數

  • 具名參數

    • 給函數的實參附上形參
    • 舉例:
      fun sum(arg1:Int, arg2:Int)= arg1+arg2
      sum (arg1=2 ,arg2=3)
      
  • 變長參數

    • 某個參數可以接收多個值
    • 可以不爲最後一個參數
    • 如果傳參時有歧義,需要使用具名參數
  • Spread Operator

    • 只支持展開Array
    • 只用於變長參數列表的實參
    • 不能重載
  • 默認參數:

    • 爲函數參數指定默認值
    • 可以爲任意位置的參數指定默認值
    • 傳參時,如果有歧義,需要使用具名參數
  • 代碼示例:

    fun main(vararg args: String){
    	var array= intArrayOf(1,3,4,5)
    	// string="Hello" 是一個默認參數
    	// *array表示將array數組展開,將一個個元素傳入,如: hello(3.0,1,3,4,5,string="Hello")
    	hello(3.0,*array,string="Hello")
    }
    	
    fun hello(double:Double, vararg ints:Int, string:String){
    	ints.forEach(::println)
    	println(string)
    }
    

3.10 導出爲可執行程序

  • 在可執行的類上加上如下字段:
apply plugin:'application'
mainClassName="net.println.kotlin.chapter3.CalcKt"

然後gradle會下載一些相關插件,完成後會在Gradle的窗口中,在Tasks-> distribution-> InstallDist 中執行InstallDist. 如圖所示:
在這裏插入圖片描述

  • 會各生成一個windows和Linux下的腳本,如圖所示:
    在這裏插入圖片描述

四. 面向對象

4.1 面向對象-抽象類與接口

  • 面向對象的基本概念

    • 本質上就是解決如何用程序描述世界的問題
    • 討論如何把實際存在的東西映射成程序的類和對象
    • 一種程序設計的思路、思想、方法
  • 類實例:

    // 定義一個類
    class Demo{
    	// 定義一個可讀變量
    	val i=4
    	// 定義一個方法
    	fun out(i: int){
    		println(i)
    	}
    }
    
    // 定義一個接口
    interface chouxianglei{
    	// 定義一個接口方法
    	fun hello()
    }
    
  • 繼承一個接口的時候,使用類名(),實現一個接口的時候,使用類名即可,在Kotlin 中是單繼承,多實現;如圖所示:
    在這裏插入圖片描述

  • 什麼是接口?

    • 接口,直觀理解就是一種約定。 Kotlin的接口與Object-C的Protocol比較類似
    • 舉例,輸入設備接口:
      interface InputDevice{
      	fun input(event: Any)
      }
      
  • 接口與抽象類的區別:

    • 接口:
      • 不能有狀態
      • 必須由類對其進行實現後使用
    • 抽象類:
      • 實現了一部分協議的半成品
      • 可以有狀態,可以有方法實現
      • 必須由子類繼承後使用
    • 共性:
      • 比較抽象,不能直接實例化
      • 有需要子類(實現類)實現的方法
      • 父類(接口)變量可以接受子類(實現類)的實例賦值

4.2 繼承

  • 繼承(實現)語法要點

    • 父類需要open纔可以被繼承
    • 父類方法、屬性需要open纔可以被覆寫
    • 接口、接口方法、抽象類默認爲open
    • 覆寫父類(接口)成員需要override關鍵字
  • Class D: A(),B,C

    • 注意繼承類時實際上調用了父類的構造方法
    • 類只能單繼承,接口可以多實現
  • class Manager(driver: Driver): Driver by driver

    • 接口方法實現交給代理類實現
  • 接口方法衝突

    • 接口方法可以有默認實現
    • 簽名一致且返回值相同的衝突
    • 子類(實現類)必須覆寫衝突方法

4.3 可見性

  • 可見性Java與Kotlin對比,如圖所示:
    在這裏插入圖片描述

4.4 Object類

  • 特點:
    • 只有一個實例的類
    • 不能自定義構造方法
    • 可以實現接口、繼承父類
    • 本質上就是單例模式最基本的實現

使用object類可以創建一個最簡單的單例類

4.5 伴生對象與靜態成員

- 伴生對象的特點:
- 每個類可以對應一個伴生對象
- 伴生對象的成員全局獨一份
- 伴生對象的成員類似Java的靜態成員
> 伴生對象就相當於靜態變量和靜態方法的整體;

- 使用伴生對象需要注意的地方:
- 靜態成員考慮用包級函數、變量替代
- @JvmField 和 @JvmStatic的使用

  • 示例代碼:
    class Latitude private constructor(val value: Double){
    	companion object{
    		@JvmStatic
    		fun ofDouble(double:Double):Latitude{
    			return Latitude(double)
    		}
    		
    		fun ofLatitude(latitude: Latitude): Latitude{
    			return Latitude(latitude.value)
    		}
    	
    		@JvmField
    		val TAG: String="Latitude"
    	}
    }
    

    這裏@JvmStatic和@JvmField不加此註解,在companion object代碼塊中均可作爲靜態方法或靜態屬性,但是如果不加不能被java代碼所識別。如果涉及到java代碼調用此靜態方法或靜態屬性,則需要加@JvmStatic或@JvmField註解;它們分別修飾方法和屬性;

4.5 方法重載與默認

  • 方法重載的特點:

    • Overloads
    • 名稱相同、參數不同的方法
    • Jvm函數簽名的概念: 函數名、參數列表
    • 跟返回值沒有關係
  • 默認參數的特點:

    • 爲函數參數設定一個默認值
    • 可以爲任意位置的參數設置默認值
    • 函數調用產生混淆時用具名參數
  • 重載與默認:

    • 二者的相關性以及@JvmOverloads
    • 避免定義關係不大的重載
    • 代碼示例如圖:
      在這裏插入圖片描述

方法重載能夠用默認參數來解決,所以能不使用就儘量不使用。@JvmOverloads能用於被java代碼所識別,如果只在Kotlin中使用可以不用加這個註解;

4.6 擴展成員

  • 在Java中,一些java庫的一些方法不夠全面,往往我們會自己定義一些Utils,在Kotlin中,我們可以對一些現有的類進行擴展;
  • 特點:
    • 爲現有成員添加方法、屬性:
      // 添加方法:
      fun X.y():Z{...}
      // 添加屬性
      val X.m 注意擴展屬性不能初始化,類似接口屬性
      
    • Java調用擴展成員類似調用靜態方法
    • 代碼如圖所示:
      在這裏插入圖片描述

4.7 屬性代理

  • 如圖所示,此懶加載就是一個代理:
    在這裏插入圖片描述

  • 特點:

    • 定義方法:
      val/var <property name>: <Type> by <expression>
      
    • 代理者需要實現相應的setValue/getValue方法

    實際上就是說,當我們使用了 val/var xxx by yyy()的屬性代理時,其代碼執行邏輯主要看yyy()了。如果是var方法我們需要實現setValue和getValue,如果是val則只需要實現getValue方法即可,因爲val是可讀常量;

  • 代碼示例:
    在這裏插入圖片描述

    其中定義了存值和賦值的get,setValue方法;當我們創建時則會自動執行setValue,當取值時則會調用getValue;

4.8 數據類

  • 特點:

    • 再見,JavaBean
    • 默認實現的copy、toString等方法
    • componentN方法
    • allOpen和noArg插件
  • 代碼示例:
    在這裏插入圖片描述

  • 同時,我們可以直接在類中直接定義構造參數值,如圖所示:
    在這裏插入圖片描述

  • 此時不需要傳入構造參數,便可將構造參數值外傳並執行其他操作,如打印,如圖所示:
    在這裏插入圖片描述

  • 數據類有一個問題,就是它沒有空的默認的構造方法。編譯生成的類是一個final的且無空構造方法,所以不是JavaBean,在有些時候使用是有問題的。我們可以通過noarg和allopen插件解決:

    1. 引入依賴:
      在這裏插入圖片描述
    2. 定義並應用插件:
      在這裏插入圖片描述
    3. 對指定的數據類使用@PoKo註解,如圖所示:
      在這裏插入圖片描述

4.9 內部類與匿名內部類

  • 特點:

    • 定義在類內部的類
    • 與類成員有相似的訪問機制
    • 默認是靜態內部類,非靜態用inner關鍵字
    • this@Outter,this@Inner的用法
  • 匿名內部類:

    • 沒有定義名字的內部類
    • 類名編譯時生成,類似Outter$1.class
    • 可繼承父類、實現多個接口,與Java注意區別
  • 定義一個內部類(非靜態):
    在這裏插入圖片描述

    非靜態內部類,需要在 類的class 前面加inner關鍵字

  • 定義一個內部類(靜態):
    在這裏插入圖片描述

    靜態內部類。默認內部類爲靜態內部類;調用時,外部類.內部類()即可;靜態內部類無法獲取非靜態的外部類的屬性和方法,因爲它是先被編譯加載的;而非靜態內部類可以持有外部類的非靜態屬性和方法;

  • 當調用外部類的屬性時,我們可以直接調用,也可以用this.@外部類名.屬性或方法來執行,如圖所示:
    在這裏插入圖片描述

  • 匿名內部類實現方法示例:
    在這裏插入圖片描述

4.10 枚舉

  • 特點:

    • 實例可數的類,注意枚舉也是類
    • 可以修改構造,添加成員
    • 可以提升代碼的表現力,也有一定的性能開銷
    • 枚舉的屬性和方法之間必須要用 ; 號隔開,這裏可能是Kotlin中唯一強制要求使用 ; 號的地方
  • 定義一個枚舉類,如圖所示:
    在這裏插入圖片描述

    $name 是它的名稱 $ordinal 是它括號裏的值

  • 調用示例:

    // 打印指定的枚舉類  1,DEBUG
    println(LogLevel.DEBUG.getTag())
    // 打印指定枚舉類的序號 1
    println(LogLevel.DEBUG.ordinal)
    // 打印指定枚舉類的實例  ERROR,4
    println(ERROR,4)
    
    

4.11 密封類

  • 特點:

    • 子類可數:
      1. Kotlin版本小於1.1時,子類必須定義爲密封類的內部類
      2. 在1.1之後,子類只需要與密封類在同一個文件中
    • 仔細體會與枚舉的不同
  • 密封類代碼示例:
    在這裏插入圖片描述

    使用枚舉適用於沒有參數的情況下,而使用密封類可以用在多參數的情況下,每個類的參數都不盡然相同,同時又想保護此類,不讓其他fun方法返回此類,就可以使用密封類;

  • 密封類在class前面加 sealed關鍵字

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章