Kotlin | 隨查指南,媽媽不再擔憂我不會Ctrl+F了

背景

學習Kotlin已經有兩年了,從今年1月開始實際投入項目使用,自認爲對於Kt使用的還算熟練。html

我的認爲Kotlin真的是一門越用越爽的語言,但在實際開發中,剛開始常常會出現忽然忘記了某個特性或者某個高階函數的使用方式,又得去google,這個時候 若是有一份筆記,可讓你直接搜索到想要的,那麼該有多好呢,這就是這份筆記的做用java

導航

本文快捷查找連接,歡迎一鍵轉存,下載,或者隨時搜索,已是MD格式了,使用 typora 盡情本地享用吧!android

碎碎念

這份總結實際上是兩篇的合集,第一篇是於2018年初學習,當時學完以後,總體上實際是懵逼的狀態,由於當時沒有實際去使用,因此在2019年末隨着工做的須要,又從新學習了一遍。固然從新學習至關於挑着看,對於一些感興趣的篇幅瞭解一下便可,更多的仍是在於實際應用。因此你會發現,這篇筆記前期皆爲基礎之談,中後期開始深刻實際使用。git

在這裏,特別感謝 bennyhuo ,是它的課程使我踏進了Kotlin的大門,有一說一,很是棒。github

一些看過的書的我的感覺:編程

  • Kotlin核心編程數組

    水滴技術團隊出的,從Kotlin的背景到各類使用細節都有概述,說它是一本神書(工具書,No)不足爲過。瀏覽器

  • Kotlin編程實戰安全

    提升實際開發效率的一本書,若是是中期去看,提升可能會很多,若是已經實際使用Kotlin許久,文中的技巧又能加深你幾分理解,即從使用轉化爲 **哦,**原來如此。markdown

  • 深刻理解Kotlin協程-bennyhuo大佬的書

    中前期不推薦,難度太高,極可能從憧憬這本書變爲罵罵咧咧的塞到顯示器底下。

一些推薦的資料:

更多

歡迎查看個人筆記,CloudBook,裏面詳細記錄了我所學所經歷的各類。

做爲一個開發者,保持不斷學習的習慣是必不可少的。咱們沒法改變時代的走向,但至少別被遠遠拉在後面,咱們可能沒法改變咱們的學歷,家室,但將來是由咱們本身來決定的。作一個永遠的學習者,永遠保持對技術的熱愛。


這裏只能摘取一部分,由於字數限制。

Kotlin學習之路

什麼是Kotlin? Kotlin學習指南

Kotlin就是一門能夠運行在 JAVA虛擬機,Android,瀏覽器上的靜態語言,它與Java100%兼容,若是你對Java很是熟悉,那麼你就會發現Kotlin除了本身的標題庫以外,大多任然使用經典的Java 集合框架。

學習目標
1. 學會使用 Kotlin
2. 熟悉Java生態
3. 瞭解一些特性背後的實現

複製代碼

Kotlin的數據類型

var與val 的區別
var爲可變變量,val至關於只讀變量,如同java 中的final 同樣,val 建立時必須被初始化。
複製代碼

1.認識基本類型 2.初步認識類及其相關概念 3.認識區間和數組

基本類型

  1. Boolean

    var a: Boolean=true
    var b: Boolean=false
    
    a爲參數
    複製代碼
  2. Number

    var a: Int =8
    var a: Int =0xFF
    複製代碼
  3. Float

    Float類型後面必須加F
    NaN /不是數
    
    val f1: Float =8.0F
    val Positive: Float = Float.POSITIVE_INFINITY	正無窮
    val Negative Float.NEGATIVE_INFINITY			負無窮
    複製代碼
  4. Short 短整型

  5. Byte

拆箱裝箱與Chart數據類型

int anInt = 5
Integer anInteger = 5
在Kotlin 裏面,Int實際是 int與Integer的合體,在必要的時候,編譯器會自動幫咱們進行分辨。
複製代碼

基礎數據類型轉換與字符串

不可隱式轉換
不能直接像Java裏同樣,將整型賦給 Long,在Kotlin 裏,須要顯示調用toLong()方法
val anInt : Int =5
val aLong : Long = abInt.toLong()
複製代碼
字符串之間的比較
== 比較內容,即相似Java 的equals
=== 比較對象是否相同
複製代碼
字符串之間的相連
//字符串模板 $
  	val a: Int = 1
    val b: Int = 2
    println("" + a + "+" + b + "=" + (a + b))
    println("$a+$b=${a+b}")
    
    若是想在字符串中添加雙引號,須要添加轉義字符
    Petterp 「123var string:String="Petterp \"123\""
    
    須要打印 $ 符號
    var money:String="1000"
    println("$$money")
    
    多行字符串,回車直接能夠換行,trimIndent()去除字符串左邊爲空的位置,這裏面沒法使用轉義
    var raw:String=""" 123 456 """.trimIndent()
    println(raw)
複製代碼

類和對象

  1. 類的寫法
    • class 類名 { 成員 }
  2. 什麼是對象
    • 是一個具體的概念,與類相對
    • 描述某一種類的具體個體
  3. 類與對象的關係
    • 一個類一般能夠有不少個具體的對象
    • 一個對象本質上只能從屬於一個類
    • 即對象與類的關係爲 1..n
    • 對象也常常被稱爲 類的對象 或者 類的實例
  4. 類的繼承
    • 提取多個類的共性獲得一個更抽象的類,即父類
    • 子類擁有父類的一切特徵
    • 子類也能夠自定義本身的特徵
    • 全部類都最終繼承自 Any
//一個繼承的例子
/*open至關於打開,容許被繼承 主構造函數不能包含任何的代碼。初始化的代碼能夠放到以 init 關鍵字做爲前綴的初始化(initializerblocks)中。 在實例初始化期間,初始化塊按照它們出如今類體中的順序執行,與屬性初始化器交織在一塊兒:*/
//constructor 主構造函數

class demo1 constructor(a: String, b: String) : Demo(a, b)
class demo2 constructor(a: String, b: String) : Demo(a, b)

open class Demo (a: String,b: String) {
    init {
    	//獲取類的名字
        println("${this.javaClass.simpleName} $a $b")
     }
}

fun main(args: Array<String>) {
    var dd1: demo2 = demo2("a", "b")
}

open 覆蓋方法也須要加
override 子類覆蓋時須要加
複製代碼

空類型和智能類型轉換

  1. 空類型
    • //?的意思是,若是爲空,則執行前半句,不然執行後半句打印長度
    • //!! 表示強制容許編譯器忽略空類型安全
java 代碼
   public class MyClass {
       public static void main(String[] a){
           System.out.println(demo().length());
       }
       public static String demo(){
           return null;
       }
   }
   
   Kotlin 代碼
   fun demo(): String? {
       //若是加了 ?,編譯器就會容許返回null,不然直接報錯
       return null
   }
   
   fun main(args: Array<String>) {
       //?的意思是,若是爲空,則執行前半句,不然執行後半句打印長度
       println(demo()?.length)
       
       val a: String? = "123"
       //!! 告訴編譯器容許編譯器忽略空類型安全
       println(a!!.length)
   }
複製代碼
  1. 安全的類型轉換
//父類強轉爲子類測試
//繼承關係,Chaid繼承自Parent

java 
public class Test {
    public static void main(String[] args) {
        Parent parent=new Parent();
        ((Chaid) parent).pp();
    }
}

Kotlin
fun main(args: Array<String>) {
    //父類強轉爲子類測試 
    val parent: Parent = Parent()
    //若是parent轉換失敗,則返回null,程序而不會崩潰
    val chaid: Chaid? = parent as? Chaid
    //此時打印即爲null
    println(chaid)
}
複製代碼

區間

  • 一個數學上的概念,表示範圍
  • ColsedRange 的子類,IntRanage 最經常使用
  • 基本寫法
    • 0..100 表示 [0,100]
    • 0 until 100 表示 [0,100)
    • i in 1..100 判斷 i 是否在區間 [0,100] 中
fun main(args: Array<String>) {
    val range:IntRange=0..1024      //[0,1024]
    val ranege_exclusive:IntRange=0 until 1024  //[0,1024)
    val range_min:IntRange= 0..-1
    
    //判斷是否是空
    println(range_min.isEmpty())

    //判斷是否包含1024
    println(ranege_exclusive.contains(1024))
    //寫法不一樣,contains 內部也是用的下面這種方法
    println(1024 in range)

    //迭代--至關於輸出range數組
    for (i in range){
        print("$i,")
    }
}
複製代碼

數組的使用方法

在Kotlin裏面,基本類型的數組,都是定製的,目的是爲了不沒必要要的裝箱與拆箱,節省效率

  • 基本寫法

    val array: Array = arrayOf(...)

  • 基本操做

    • print array[i] 輸出第 i 個成員
    • array[i] = "Hello" 給第i 個成員賦值
    • array.size 數組的長度
val arrayOfInt: IntArray = intArrayOf(1, 3, 5)
val arrayOfChar: CharArray = charArrayOf('H', 'e', 'l', 'l', 'o')
val arrayOfString: Array<String> = arrayOf("Petterp", "p")
fun main(args: Array<String>) {
    //for迭代
    for (int in arrayOfInt) {
        println(int)
    }
    //返回[0,2)區間的元素
    println(arrayOfString.slice(0 until 2))
    //使用分隔符建立一個字符串
    println(arrayOfChar.joinToString(""))
    //將指定數組中的字符轉換爲字符串
    println(String(arrayOfChar))
    //打印類的全名
    println(Kotlin2::class.java.name)
    //打印類名區間
    println(Kotlin2::class.java.simpleName.slice(0 until 2))
    //基本類型轉換
    val hello:Long= arrayOfChar.size.toLong()
    println(hello)
}

class Kotlin2 {

}

複製代碼

程序結構

常量與變量

  • val 聲明常量,相似於 Java 的final 關鍵字,不可被重複賦值
  • 在Kotlin 裏面有類型推導,編譯器能夠推導量的類型,因此可推導類型定義時能夠不用寫數據類型,
  • 運行期常量 val x=getX() 利用反射手段是能夠修改值
  • 編譯器常量 const val x=1 代碼在編寫過程當中已經肯定了值,代碼中引用到的位置,都替換成了 x,能夠在字節碼中看到
  • var 聲明變量
val data: String = "Petterp"
val data2 = data
//類型推導,編譯器已經知道它的類型是 Int
val data3 = 1
fun main(args: Array<String>) {
    println(data)
    println(data2)
    println(data3)
}
複製代碼
如今添加 const 

const val data: String = "Petterp"
const val data2 = data
//類型推導,編譯器已經知道它的類型是 Int
val data3 = 1
fun main(args: Array<String>) {
    println(data)
    println(data2)
    println(data3)
}
複製代碼

因此這就是編譯器常量的做用。編譯器常量的引用都是直接指明該變量的值


函數

//函數寫法
fun Demo1(a: Int): Int {
    return a
}
//當函數返回一個表達式的值是,可使用一下方式
fun Demo2(a: Int) = a

//匿名函數,須要賦值給一個函數變量
val k = fun(a: Int): Int {
    return a
}

fun main(args: Array<String>) {
    println(Demo1(100))
    println(Demo2(200))
    println(k(300))
}
複製代碼

注意事項

  • 功能要單一
  • 函數名要作到顧名思義
  • 參數個數不要太多

Lambda表達式(很是重要)

  1. 什麼是Lambda表達式

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

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

    • 用 () 進行調用

    • 等價於 invoke()

    • 舉例
      val sum{a:Int,b:Int -> a+b}
      
      調用
      sum(1,2)
      sum.invoke(1,2)
      複製代碼
  4. Lambda 表達式的簡化

    • 函數參數調用時最後一個 Lambda 能夠移出去
    • 函數參數只有一個Lambda,調用時小括號可省略
    • Lambda 只有一個參數 可默認爲 it
    • 入參,返回值與形參一直的函數能夠用函數引用的方式做爲實參傳入
    //Lambda
    //原始寫法
    args.forEach ({ println(it) })
    //對於函數來講,若是最後一個參數是Lambda表達式,小括號能夠移到外面
    args.forEach(){ println(it) }
    //若是小括號裏什麼都沒有,能夠刪掉小括號
    args.forEach { println(it)}
    //若是傳入的這個函數和須要接收的Lambda表達式類型同樣,那麼進一步簡化
    args.forEach (::println)
    複製代碼

類成員(成員方法,成員變量)

  1. 什麼是類成員

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

    • 函數強調功能自己,不考慮從屬
    • 方法的稱呼一般是從類的角度出發
  3. 定義方法

    寫法與普通函數徹底一致
    class A {
        //簡短寫法,沒有返回值類型的狀況下
        fun say(name: String) = println("Hello $name")
        fun phone(phone:String):String{
           return phone
        }
       
    }
    複製代碼
  4. 定義屬性

    • 構造方法參數中 val / var 修飾的都是屬性

    • 類內部也能夠定義屬性

    • // 加修飾的爲屬性,b只是普通的一個構造方法參數
      class A(val a: Int, b: Int) {
          var b = 0
      }
      複製代碼
  5. 屬性訪問控制

    • 屬性能夠定義 getSet / setter

    • class B{
          //val 修飾的 不可變 無set方法
          val demo:Int=0
       /* set(value) { }*/
          var demo2:Int=0
          set(value) {
              println(demo2)
              field = value
          }
      }
      複製代碼
  6. 屬性初始化

    • 屬性的初始化儘可能在構造方法中完成

    • 沒法在構造方法中初始化,嘗試降級爲局部變量

    • var 用 lateinit 延遲初始化,val 用 lazy

    • 可空類型謹慎用 null 直接初始化

    • class X
      class A(val a: Int, b: Int) {
          var b = 0
          //laterinit 延遲初始化
          lateinit var c: String
          lateinit var d: X
          val e: X by lazy {
              println("Start X")
              X()
          }
          var cc: String? = null	//不推薦這種寫法
      }
      複製代碼

基本運算符

  • 任意類能夠定義或者重載父類的基本運算符

  • 經過運算符對應的具名函數來定義

  • 對於參數個數作要求,對參數和返回值類型不作要求

  • 不能像Scala同樣定義任意運算符

    //使用operator關鍵字能夠重載基本運算符,好比下面的plus函數加上operator,就至關於基本運算中的 +
    //運算符重載要求與運算符的函數名對應,好比要重載加法,函數名就必須是 plus
    
    class Complex(var real:Int,var imaginzry:Int){
        operator fun plus(other:Complex):Complex{
            return Complex(real+other.real,imaginzry+other.imaginzry)
        }
    	
        operator fun plus(other: Int):Complex{
            return Complex(real+other,imaginzry)
        }
        operator fun plus(other: Any):Int{
           return real.toInt()
        }
    
        override fun toString(): String {
            return "$real+$imaginzry"
        }
    }
    
    class Book{
        //infix 中指表達式,不用點括號去調用
        infix fun on(any:Any):Boolean{
            return false
        }
    }
    class Desk
    
    fun main(args: Array<String>) {
        val c1=Complex(3,4)
        val c2=Complex(1,2)
        println(c1+c2)
        println(c1+5)
        println(c1+"Petterp")
    	
        //-name <Name>
        //in 表示有這個元素返回1 不然返回-1
        if ("-name" in args){
            println(args[args.indexOf("-name")+1])
        }	
       
    
    }
    
    
    複製代碼

表達式

  1. 中綴表達式

    • 只有一個參數,且用 infix 修飾的函數

    • //infix(中綴表達式) 定義的函數沒必要動過.xx() 的形式調用,而是能夠經過函數名(沒必要寫括號) 調用。這種寫法在Dsl 中比較常見,在代碼中慎用,影響可讀性
      class Book{
          //infix 
          infix fun on(any:Any):Boolean{
              return false
          }
      }
      class Desk
      
      
      fun main(args: Array<String>) {
      
          if(Book() on Desk()){
      
          }
      
      }
      複製代碼
  2. if表達式

    • if ..else 同java 同樣,可是Kotlin 裏面具備返回值,因此稱爲表達式

    • 表達式與完備性 (即在用 if表達式 賦值時,全部條件都必須完整)

    • val x=if(b<0) 0 else b
      val x=if(b<0) 0 //錯誤,賦值時,分支必須完備
      複製代碼
  3. when表達式

    • 增強版 switch,支持任意類型

    • 支持春表達式條件分支(相似 if)

    • 表達式與完備性

    • fun main(args: Array<String>) {
          val x=99
          val b=when(x){
              in 1..100 ->  10
              !in  1..100 -> 20
              args[0].toInt() -> 30
              else -> x
          }
          when(x){
              in 1..100 -> println("ok")
              else -> println("no")
          }
      // val mode=when{
      // args.isNotEmpty()&& args[0]=="1" ->1
      // else ->0
      // }
          println(b)
      }
      複製代碼

for循環

fun main(args: Array<String>) {
    for((i,value) in args.withIndex()){
        println("$i -> $value")
    }

    for(i in args.withIndex()){
        println("${i.index} -> ${i.value}")
    }
}
複製代碼

異常捕獲

  1. try...catch

    • catch 分支匹配異常類型

    • 能夠寫爲表達式,用來賦值

    • val  a=try {
              100/10
          }catch (e:Exception){
              0
          }
      複製代碼
  2. finally

    • finallu 不管代碼是否拋出異常都會執行

    • //注意下面的寫法finaly仍是會先執行,最後纔是 return
       fun P(): Int {
          //try 表達式
          return try {
              100 / 10
          } catch (e: Exception) {
              0
          } finally {
              println("我先打印")
          }
      複製代碼

具名參數,變長參數,默認參數

  1. 具名參數

    • 給函數的實參附上形參

    • //這樣寫的話,參數的位置就不會產生影響
      
      fun main(args: Array<String>) {
          sum(b=1,a=2)
      }
      
      fun sum(a: Int, b: Int) {
          println("a=$a b=$b")
      }
      複製代碼
  2. 變長參數

    • 某個參數能夠接受多個值

    • 能夠不爲最後一個參數

    • 若是傳參時有歧義,須要使用具名參數

    • fun main(vararg: Array<String>) {
          sum(1, 2, 3, 4, b = "B")
      }
      
      fun sum(vararg a: Int, b: String) {
          a.forEach(::println)
          println(b)
      }
      複製代碼
  3. Spread Operator

    • 只支持展開 Array

    • 只用於變長參數列表的實參

    • 不能重載

    • 不能算通常的運算符

    • fun main(vararg: Array<String>) {
          sum(1, 2, 3, 4, b = "B")
      
          //Spread Operator,只支持Array
          val array= intArrayOf(1,3,4,5)
          sum(*array,b="B")
      }
      
      fun sum(vararg a: Int, b: String) {
          a.forEach(::println)
          println(b)
      }
      複製代碼
  4. 默認參數

    • 爲函數參數指定默認值

    • 能夠爲任意位置的參數指定默認值

    • 傳參時,若是有歧義,須要使用具名參數

    • fun main(vararg: Array<String>) {
          //調用者未傳值,使用的是默認值
          sum(b="B")
          sum(1,"B")
      }
      
      //若是有個函數常用一個值,那麼在其聲明的時候就能夠指定這個值
      fun sum(a: Int = 0, b: String) {
          println("a=$a b=$b")
      }
      複製代碼

面向對象

抽象類與接口

  1. 什麼是接口

    • 接口,直觀理解就是一種約定

    • interface A {
          fun Print()
      }
      
      //接口繼承
      interface A1 : A {
          //也能夠定義一個變量,這裏實際至關於方法,可是沒法有set,get方法
          var a: Int
          fun ko() {
              println(a)
          }
      }
      
      class TestA(override var a: Int) : A1 {
          override fun Print() {
              println("Petterp")
          }
      }
      
      fun main() {
          val test = TestA(1)
          test.Print()
          test.ko()
      }
      複製代碼
  2. 接口

    • 接口不能有狀態
    • 必須由類對其進行實現後使用
  3. 抽象類

    • 實現了一部分協議的半成品

    • 能夠有狀態,能夠有方法實現

    • 必須由子類繼承後使用

    • interface P1
      
      interface P2 : P1
      
      abstract class A {
          //打印出類名
          fun Print() = println(javaClass.simpleName)
      }
      
      class B : A(), P2
      
      fun main() {
          //不能夠這樣用,由於沒有子類繼承
      // val a = A
      
          //關係能夠這樣來寫
          val p1: P1 = B()
          val p2: P2 = B()
          val a: A = B()
          val b: B = B()
          b.Print()
      }
      複製代碼
  4. 抽象類與接口的共性

    • 比較抽象,不能直接實例化
    • 有須要子類(實現類) 實現的方法
    • 父類 (接口) 變量能夠接受子類 (實現類) 的實例賦值
  5. 抽象類和接口的區別

    • 抽象類有狀態,接口沒有狀態
    • 抽象類有方法實現,接口只能有無狀態的默認實現
    • 抽象類只能單繼承,接口能夠多實現
    • 抽象類反映本質,接口體現能力

繼承

  • 父類須要open 才能夠被繼承

  • 父類方法,屬性須要open 才能夠被覆寫

  • 接口,接口方法,抽象類默認爲 open

  • 覆寫父類 (接口) 成員 須要 onverride 關鍵字

  • class D:A() , B ,C
    複製代碼
  • 注意繼承類時實際上調用了父類構造方法

  • 類只能單繼承,接口能夠多實現

  • //繼承與子類重寫父類的Demo
    
    abstract class Person(open val age: Int) {
        abstract fun work()
    }
    
    class MaNong(age: Int) : Person(age) {
        //重寫屬性
        override val age:Int
        get() = 0
        override fun work() {
            println("我是碼農")
        }
    }
    
    class Doctor(age: Int) : Person(age) {
        override fun work() {
            println("我是醫生")
        }
    }
    
    fun main() {
        val manong = MaNong(20)
        val doctor = Doctor(100)
        println(manong.age)
        println(doctor.age)
    }
    複製代碼
  • 接口代理

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

  • class Manager(driver:Driver):Driver by driver
    
    //接口代理的Demo
    
    interface AA{
        fun Print1()
    }
    interface BB{
        fun Print2()
    }
    
    class Car:AA{
        override fun Print1() {
            println("AA")
        }
    }
    
    class Bar:BB{
        override fun Print2() {
            println("BB")
        }
    }
    //
    //class AB:AA,BB{
    // override fun Print1() {
    // TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    // }
    //
    // override fun Print2() {
    // TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    // }
    //
    //}
    
    //by 接口代理
    class SendWill(val aa:AA,val bb:BB):AA by aa,BB by bb
    
    fun main() {
        val aa=Car()
        val bb=Bar()
        val send=SendWill(aa,bb)
        send.Print1()
        send.Print2()
    }
    複製代碼
  • 接口方法衝突

  • 接口方法能夠有默認實現

  • 簽名一致且返回值相同的衝突

  • 子類(實現類) 必須覆寫衝突方法

  • super <[父類 (接口)  名 ]>.[方法名]([參數列表]
    複製代碼
  • Demo
    interface A {
        fun setT() = "接口A"
    }
    
    interface B {
        fun setT() = "接口B"
    }
    
    abstract class C {
        open fun setT() = "抽象類C"
    }
    
    class Demo(val a: Int) : A, B, C() {
        override fun setT(): String {
            return when (a) {
                1 -> super<A>.setT()
                2 -> super<B>.setT()
                3 -> super<C>.setT()
                else -> ""
            }
        }
    }
    
    fun main() {
        println(Demo(1).setT())
        println(Demo(2).setT())
    }
    複製代碼

類及其成員可見性

1550315847886

Object(最簡單的單例模式)

  • 只有一個實例的類

  • 不能自定義構造方法

  • 能夠實現接口,繼承父類

  • 本質上就是單例模式最基本的實現

  • Koltin--
    interface A {
        fun a1() } abstract class C object KotlinDan : C(), A {
        override fun a1() {
            println("Kotlin")
        }
    }
    
    
    Java--
    public class Kotlin_java {
        public static void main(String[] args) {
            //引用Kotlin代碼
            KotlinDan.INSTANCE.a1();
            //在java裏面
            JavaDan.inter.Ag();
        }
    }
    
    //java中的簡易單例例子
     class JavaDan{
        static JavaDan inter=new JavaDan();
        private JavaDan(){}
        void Ag(){
            System.out.println("Java");
        }
     }
    複製代碼

伴生對象與靜態成員

  • 每一個類能夠對應一個伴生對象

  • 伴生對象的成員全局獨一份(對於類來講)

  • 伴生對象的成員相似 Java 的靜態成員

  • 在Kotlin中,靜態成員考慮用包級函數,包級變量替代

  • JvmField 和 JvmStatic 的使用

  • Kotlin--
    class Demo private constructor(val value:Double){
        //伴生對象
        companion object {
            @JvmStatic
            fun A(double: Double): Demo {
                return Demo(double)
            }
            @JvmField
            val TAG:String="Petterp"
        }
    }
    
    fun main() {
        println(Demo.A(1.1).value)
    }
    
    Java--
    
    public class KotlinJava {
        public static void main(String[] args) {
            //java代碼,未添加 @JvmStataic 與 JvmField
            Demo demo = Demo.Companion.A(1.1);
            String tag = Demo.Companion.getTAG();
    
            //添加後
            Demo demo1 = Demo.A(1.1);
            String  tag1=Demo.TAG;
        }
    }
    
    複製代碼

方法重載與默認參數

  1. 方法重載

    • Overloads

    • 名稱相同,參數不一樣的方法

    • Jvm函數簽名的概念 : 函數名,參數列表

    • 跟返回值沒有關係

    • class A{
          fun a():Int{
              return 0
          }
          fun a(int: Int):Int{
              return int
          }
      
          //方法重載與返回值無關
         /* fun a():String{ }*/
      }
      
      fun main() {
          val a=A().a()
          val a2=A().a(123)
      
      }
      複製代碼
  2. 默認參數

    • 爲函數參數設定一個默認值

    • 能夠爲任意位置的參數設置默認值

    • 函數調用產生混淆時用具名參數

    • Kotlin--
      class A {
      // fun a():Int{
      // return 0
      // }
      // fun a(int: Int):Int{
      // return int
      // }
      
          //使用具名參數替代上面的重載方法
          @JvmOverloads	
          fun a(int: Int = 0): Int {
              return int
          }
      }
      
      fun main() {
          val a = A().a()
          val a2 = A().a(123)
      }
      
      Java調用時--
      public class KotlinJava {
          public static void main(String[] args) {
              A a=new A();
              //在Kotlin代碼上添加 @JvmOverloads 就可讓java識別具名參數
              a.a();
              a.a(123);
          }
      }
      複製代碼
  3. 方法重載與默認參數的關係

    • 兩者的相關性以及 @JvmOverloads

    • 避免定義關係不大的重載

    • 很差的設計好比:

      Java代碼中的一個Bug
      public class Bug {
          public static void main(String[] args) {
              List<Integer> list=new ArrayList<>();
              list.add(1);
              list.add(2);
              list.add(10);
              list.add(20);
              System.out.println(list);
              //異常,數組越界
              list.remove(10);
              list.remove(2);
          }
      }
      
      在Kotlin代碼中
      fun main() {
          val list = ArrayList<Int>()
          list.add(1)
          list.add(2)
          list.add(10)
          list.add(20)
          println(list)
          list.removeAt(10)
          list.remove(2)
      }
      複製代碼

擴展成員

  • 爲現有類添加方法,屬性

  • fun X.y():Z {...}
    val X.m 注意擴展屬性不能初始化,相似接口屬性
    複製代碼
  • Java 調用擴展成員相似調用靜態方法

  • operator fun String.times(int: Int):String{
        val stringBuilder=StringBuilder()
        for (i in 0 until  int){
            stringBuilder.append(this)
        }
        return stringBuilder.toString()
    }
    
    val String.op:String
        get() = "123"
    
    fun main() {
        println("abc".times(16))
        //擴展方法前加了operator,重載運算符
        println("abc"*16)
        
        println("abc".op)
    }
    
    
    java調用
    public class A {
        public static void main(String[] args) {
            System.out.println(Kotlin2Kt.times("abc",16));
            System.out.println(Kotlin2Kt.getOp("Petterp"));
        }
    }
    
    複製代碼

屬性代理

  • 定義方法

    val/var <property name>: <Type> by <experession> 
    //翻譯 property-屬性 type-類型 experssion-函數調用
    複製代碼
  • 代理者須要實現相應的 setValue/getValue 方法

  • // Demo--
    class A{
        val p1:String by lazy {
            "123"
        }
        //屬性代理,實際調用getValue
        val p2 by X()
        //須要實現setValue
        var p3 by X()
    }
    class X{
        private var value: String? =null
        operator fun getValue(thisRef:Any?,property:KProperty<*>):String{
           return value?:""
        }
        operator fun setValue(thisRef: Any?,property: KProperty<*>,string: String){
            this.value=string
        }
    }
    
    fun main() {
        val a=A()
        println(a.p1)
        println(a.p2)
        //此時爲空,由於未設置值
        println(a.p3)
        a.p3="123"
        println(a.p3)
    }
    複製代碼

數據類

  • 再見,JavaBean

  • 默認實現的copy,toString 等方法

  • componentN 方法

  • allOpen 和 noArg 插件 (設計的角度來講,data class不容許有子類,因此若是你要改寫的話,須要用到這兩個插件,會在編譯期經過修改字節碼的方式去掉 final關鍵字,並增長一個無參構造方法)

  • data class 初始化的時候,必定要給它的屬性賦值(帶參數),即它並無默認的無參構造方法

  • //加了data 以後,自動實現各類方法,可查看字節碼發現
    data class A(val id:Int,val name:String)
    class ComponentX{
        //手動實現component1()
        operator fun component1():String{
            return "PP"
        }
        operator fun component2():Int{
            return 0
        }
    }
    fun main() {
        println(A(1,"Petterp"))
        val (a,b)=ComponentX()
        println("a=$a,b=$b")
        //打印結果
        /*A(id=1, name=Petterp) a=PP,b=0*/
    }
    複製代碼
    allOpen noArg 配置方法
    
    複製代碼

內部類(this@Outter,this@Inner)

  1. 內部類

    • 定義在類內部的類

    • 與類成員有類似的訪問控制

    • 默認是靜態內部類,非靜態用 inner 關鍵字

    • this@Outter , this@Inner 的用法

    • 若是內部類依賴外部類,那麼使用非靜態內部類,不然反之

    • class A {
          var int:Int = 5
      
          //加了inner 爲非靜態內部類
          inner class A1 {
              var int:Int = 10
              fun Print(){
                  //內部類的變量
                  println(int)
                  //外部變量
                  println(this@A.int)
              }
          }
      }
      複製代碼
  2. 匿名內部類

    • 沒有定義名字的內部類

    • 類名編譯時生成,相似 Outter$1.class (看起來沒有名字,實際編譯時有本身的Id)

    • 可繼承父類,實現多個接口,與Java 注意區別

      interface OnclickListener {
          fun onclick()
      }
      
      class View {
          var onclickListener: OnclickListener? = null
      }
      
      open class A
      
      fun main() {
          val view=View()
          view.onclickListener=object : A(),OnclickListener{
              override fun onclick() {
                  
              }
          }
      }
      複製代碼

枚舉

  • 實例可數的類,注意枚舉也是類

  • 能夠修改構造,添加成員

  • 能夠提高代碼的表現力,也有必定的性能開銷

  • //枚舉類也是有構造方法的,咱們能夠在它的構造方法中傳入參數
    enum class LogLevel(val id:Int){
        A(1),B(2),C(3),D(4),E(5) ;
        //須要分號隔開
        fun getTag():String{
            return "$id,$name"
        }
    	
        override fun toString(): String {
            return "$name,$ordinal"
        }
    }
    
    //LogLevel 更像是 LogLevel2的語法糖
    //它們兩個是等價的
    class LogLevel2(val id:Int) private constructor(){
        //伴生對象寫法
        companion object {
            val A=LogLevel2(1)
            val B=LogLevel2(2)
            val C=LogLevel2(3)
            val D=LogLevel2(4)
            val E=LogLevel2(5)
        }
    
        constructor(i: Int) : this()
    }
    
    fun main() {
        println("${LogLevel.A},${LogLevel.A.ordinal}")
        println(LogLevel.A.getTag())
        //遍歷枚舉,重寫了toString()方法
        LogLevel.values().map(::println)
    
        //返回"A"的實例
        println(LogLevel.valueOf("A"))
    }
    複製代碼

密封類(sealed Class)

  • 子類可數 (枚舉是實例可數)

  • 要注意密封類與枚舉的不一樣,看如下Demo

  • //在如下Demo中,這是一個音樂播放Demo
    //須要不一樣指令及不須要參數的地方咱們能夠用枚舉實現,而那些須要不一樣指令參數的地方咱們用枚舉就沒法實現了
    
    //sealed的子類只能繼承在與Sealed同一個文件當中,或者做爲它的內部類
    sealed class PlayerCmd {
        class Play(val yrl: String, val postition: Long = 0) : PlayerCmd()
    
        class Seek(val postition: Long) : PlayerCmd()
    
        object Pause : PlayerCmd()
    
        object Resume : PlayerCmd()
    
        object Stop : PlayerCmd()
    }
    
    enum class PlayerState{
        IDLE,PAUSE,PLAYING
    }
    複製代碼

高階函數

高階函數的基本概念

  • 傳入或者返回函數的函數

  • 函數引用 ::println

  • 帶有 Receiver 的引用, pdfPrinter :: println

  • fun main(args: Array<String>) {
        //包級函數引用
        args.forEach(::println)
    
        //類引用
        val helloWorld = Hello::world
    // args.filter(String::isNotEmpty)
    
        //調用者引用方法
        args.forEach(PdfPrinter()::println)
    
        //Demo1
        println(demo("k",::getRest))
        //Demo2
        demo2(Hello()::world)
    }
    
    class Hello {
        fun world(){
            println("123")
        }
    }
    
    class PdfPrinter {
        fun println(any: Any) {
            kotlin.io.println(any)
        }
    }
    
    fun getRest(s1:String):String="Petterp $s1"
    
    fun demo(a: String, method: (s1: String) -> String): String = method(a)
    
    
    fun demo2(methoud2:()->Unit)=methoud2()
    複製代碼

經常使用的高階函數

  • forEach (一般用於遍歷集合)

    val list = listOf(1, 3, 4, 5, 6, 7, 8)
        val newList=ArrayList<Int>()
        list.forEach {
            newList.add(it*2)
        }
        newList.forEach(::println)
    複製代碼
  • map (用於集合的映射,還能夠用於集合轉換)

    val list = listOf(1, 3, 4, 5, 6, 7, 8)
      val newList = list.map { it * 2 + 3 }
      newList.forEach(::println)
    複製代碼
  • flatmap (用於吧集合的集合扁平化成集合,換能夠結合map進行一些變換)

    fun main() {
      .flatMap { intRange ->
            intRange.map { intElement ->
                "No.$intElement"
            }
        }.forEach(::println)
        
        //簡寫
        list.flatMap { 
            it.map { "No.$it" }
        }.forEach(::println)
    }
    複製代碼
  • fold (用於求和並加上一個初始值由於fold不一樣於map,fold對初始值沒有嚴格限制,所以fold還能夠進行類型變換)

    fun main() {
        //遍歷0..6的階乘
        (0..6).map(::factorial).forEach(::println)
    
        //加初始值5 結果: 879
        println((0..6).map(::factorial).fold(5){acc, i ->acc+i})
        //加上psotion
        println((0..6).map(::factorial).foldIndexed(StringBuilder()){postion,acc, i ->acc.append("$i-$postion,")})
        //更改返回類型 結果 1,1,2,6,24,120,720,
        println((0..6).map(::factorial).fold(StringBuilder()){acc, i -> acc.append("$i,") })
        //倒序
        println((0..6).map(::factorial).foldRight(StringBuilder()){i,acc -> acc.append(i).append(",") })
        
        //字符串鏈接
        println((0..6).joinToString(","))
    }
    
    //求階乘
    fun factorial(n:Int):Int{
        if (n==0){
            return 1
        }
        return (1..n).reduce { acc, i -> acc*i}
    }
    複製代碼
  • reduce (求和)

    fun main() {
        val list = listOf(
            1..20,
            6..100,
            200..220
        )
        //求和 結果:9655
        println(list.flatMap { it }.reduce { acc, i -> acc + i })
    }
    複製代碼
  • filter(用於過濾,若是傳入的表達式值爲true,就保留)

    val list = listOf(
           1,3,2
        )
        //傳入的表達式值爲true就保留
        println(list.filter { it%2==0 })
        //知足條件的保留,並有他們的位置
        println(list.filterIndexed{index, i ->  i%2==1 })
    複製代碼
  • take (一般爲帶有條件的循環遍歷)

    val list = listOf(
           1,3,2
        )
      	//從第一個元素開始,只返回符合的元素,遇到不符合中止
        println(list.takeWhile { it%2==1 })
        //從最後一個元素開始,一直取到不符合的就返回前面的元素
        println(list.takeLastWhile { it%2==0})
        //返回最後一個元素到指定元素位置的列表,不包含指定位置元素
        println(list.takeLast(4))
        //返回第一個一個元素到指定元素位置的列表,不包含指定位置元素
        println(list.take(4))
        //參數是個方法,返回值是一個布爾類型,爲真返回對象T,不然返回null
        println(list.takeIf { it.size>6 })
    複製代碼
  • let,apply,with,use(用於簡化代碼,use能夠簡化 colse,try/catch 統一使用模板)

    data class Person(val name: String, val age: Int) {
        fun work() {
            println("我是$name")
        }
    }
    
    fun main() {
        findPerson()?.let {
            it.work()
            println(it.age)
        }
    
        //apply至關於一個擴展方法
        findPerson()?.apply {
            work()
            println(age)
        }
    
        BufferedReader(FileReader("A.txt")).use {
            var line:String?
            while (true){
                line=it.readLine()?:break
                println(line)
            }
        }
    
    // val br=BufferedReader(FileReader("hello.txt")).readLine()
    }
    
    fun findPerson(): Person? {
        return null
    }
    複製代碼

尾遞歸優化

尾遞歸指的是函數調用本身以後沒有任何操做,也就是一個函數中全部遞歸形式的調用都出如今函數的末尾。尾遞歸可使用 tailrec 關鍵字優化

  • 遞歸的一種特殊形式

  • 調用自身後無其餘操做

  • talrec關鍵字 提示編譯器尾遞歸優化

  • 尾遞歸能夠直接轉換爲迭代

  • data class ListNode(val value:Int,var next:ListNode?=null)
    
     tailrec fun findListNode(head:ListNode?,value:Int):ListNode?{
        head?:return null
        if (head.value==value) return head
        return findListNode(head.next,value)
    }
    
    
    fun main() {
        val Max_Node_Court=1000000
        val head=ListNode(0)
        var p=head
        for (i in 1..Max_Node_Court){
            p.next= ListNode(i)
            p=p.next!!
        }
        println(findListNode(head,Max_Node_Court-2)?.value)
    }
    
     fun factorial(n:Long):Long{
        return n* factorial(n-1)
    }
    
    data class TreeNode(val value:Int){
        var left:TreeNode?=null
        var right:TreeNode?=null
    }
    
     fun findTreNode(root:TreeNode?,value: Int):TreeNode?{
        root?:return  null
        if (root.value==value) return root
        return findTreNode(root.left,value)?:return findTreNode(root.right,value)
    }
    複製代碼

閉包

  • 函數運行的環境

  • 它持有函數運行的狀態

  • 函數內部能夠定義函數

  • 函數內部也能夠定義類

  • val String="HelloWord"
    
    //Lambda 返回一個無參函數
    fun makeFun():() -> Unit{
        var count=0
        return fun (){
            println(++count)
        }
    }
    
    fun main(args: Array<String>) {
    // val x= makeFun()
    // x()
    // x()
        val add5= add(5)
        println(add5(2))
    
        for (i in fibonac()){
            if (i>100) break
            println(i)
        }
    }
    
    
    fun fibonac():Iterable<Long>{
        var first=0L
        var second=1L
        return Iterable {
            object :LongIterator(){
                override fun hasNext()=true
                override fun nextLong(): Long {
                        val result=second
                        second+=first
                        first=second-first
                        return result
                }
    
            }
        }
    }
    
    //Demo2
    //簡寫
    fun add(x:Int)=fun (y:Int)=x+y
    //完整
    fun add2(x: Int):(Int)->Int{
        return fun (y:Int):Int{
            return x+y
        }
    }
    
    複製代碼

函數複合

  • f(g(x))

  • val add5={i:Int -> i+5}     //f(g(x))
    val mulyiplyBy2={i:Int ->i*2} // m(x)=f(g(x))
    
    fun main(args: Array<String>) {
    
        println(mulyiplyBy2(add5(8)))       //(5+8)*2
    
        val k= add5 andThen mulyiplyBy2
        println(k(8))
    }
    
    
    
     /*P1,P2 表示參數值,R表示返回值 addThen爲擴展方法 infix中綴表達式 Function<P1,P2> p1爲參數類型,P2爲返回值類型 */
    infix  fun <P1,P2,R > Function1<P1,P2>.andThen(function: Function1<P2,R>):Function1<P1,R>{
        return fun (p1:P1):R{
            //返回了一個函數,函數裏面又把function這個參數調用了一遍
            //調用的時候又把本身調用了一遍,把本身的返回值傳給function
            return function.invoke(this.invoke(p1))
        }
    }
    
    infix  fun <P1,P2,R> Function1<P1,P2>.compose(function: Function1<P2,R>):Function1<P1,R>{
        return fun (p1:P1):R
        {
            return function.invoke(this.invoke(p1))
        }
    }
    複製代碼

柯里化

  • Currying 簡單來講就是多元函數變換成一元函數調用鏈

  • //由多參數變換爲單參數的變化
    fun Test(a: Int): (String) -> (Int) -> Boolean {
        return fun(b: String): (c: Int) -> Boolean {
            return fun(c: Int): Boolean {
                return true
            }
        }
    }
    
    fun log(tag: String, target: OutputStream, message: Any?) {
        target.write("[$tag] $message\n".toByteArray())
    }
    
    //柯里化
    fun log2(tag: String)
            = fun(target: OutputStream)
            = fun(message: Any?)
            = target.write("[$tag] $message\n".toByteArray())
    
    fun main() {
        log("benny",System.out,"Heelo2wor")
        log2("Petterp")(System.out)("Demo")
    
        ::log.curried()("Petterp")(System.out)("Demo")
    }
    
     fun <P1,P2,P3,R> Function3<P1,P2,P3,R>.curried()
        =fun(p1:P1)=fun (p2:P2)=fun (p3:P3)=this(p1,p2,p3)
    複製代碼

偏函數

  • 傳入部分參數獲得的新函數

  • 對於某些傳值比較固定的參數,偏函數能夠將其綁定,而後生成新的函數,而新的函數只須要給除已綁定的參數以外的參數傳值,固然你也能夠視同 默認參數+具名參數 的方式來實現參數的固定,若是須要固定的參數在中間,雖說能夠經過具名參數來解決,可是很尷尬,由於必須使用一大推具名參數,所以偏函數就誕生了

  • val test=fun(name:String,love:String):String{
        return "${name}$love"
    }
    
    fun <P1,P2,R> Function2<P1,P2,R>.partial1(p1:P1)=fun(p2:P2)=this(p1,p2)
    fun <P1,P2,R> Function2<P1,P2,R>.partial2(p2:P2)=fun (p1:P1)=this(p1,p2)
    
    fun main() {
        val t= test.partial1("Petterp")
        println(t("寫Bug"))
    
        val t2= test.partial2("改Bug")
        println(t2("Petterp"))
    }
    複製代碼

泛型

泛型的基本語法

  • 泛華的類型或者說類型的抽象
  • 鴨子類型是動態類型和靜態語言的一種對象推斷分格,在鴨子類型中,關注的不是對象的類型自己,而是他是如何使用的,也就是說咱們只關注它的行爲。

java與Kotlin 互操做

基本互操做

  1. 空安全類型

    • Kotlin空安全類型原理

      Kotlin在編譯的時候,會增長一個函數調用,會對參數類型,返回值類型進行是否爲null的檢查
      複製代碼
    • 平臺類型 PlatFromType

      由於Java裏面並無空安全類型,因此可能會出現平臺類型的問題,這時候就須要咱們開發者本身明白鬚要使用的參數是否能夠爲null
      複製代碼
    • @Nullable 和 @NotNull

      在開發Java代碼的時候,能夠經過註解的方式來彌補這一點
      複製代碼
  2. 幾類函數的調用

    • 包級函數:靜態方法

      在java裏並無這種函數,它在編譯的時候,會爲Kotlin生成一個類,這個類包含了全部包級函數,在java看來,這些都只是靜態方法,因此在java調用的時候,按照靜態按方法調用便可
      複製代碼
    • 擴展方法:帶 Receiver 的靜態方法

      擴展方法只是增長了一個 Receiver 做爲參數
      複製代碼
    • 運算符重載:帶Receiver 的對應名稱的靜態方法

  3. 經常使用註解的使用

    • @JvmField : 將屬性編譯爲 JAVA變量

    • @JvmStataic :將對象的方法編譯成 Java靜態方法

    • @JvmOverloads : 默認參數生成重載方法

      若是一個參數帶有默認參數,Java實際是看不見的
      複製代碼
    • @file: JvmName : 指定Kotlin 文件編譯後的類名

  4. NoArg 與 AllOpen

    • NoArg 爲被標註的類生成無參構造

      支持Jpa註解,如 @Entity
      複製代碼
    • AllOpen 爲被標註的類去掉final,容許被繼承

      支持 Spring註解,如 @Component
      複製代碼
    • 支持自定義註解類型,例如 @Poko

  5. 泛型

    • 通配符 Kotlin 的 * 對應java的 ?

    • 協變與逆變 out/in

      ArrayList<out String>
      複製代碼
    • 沒有Raw 類型

      java 的List-> 		Kotlin的List<*>
      複製代碼

Kotlin-重構篇-更加接近實際應用

類和接口

定義一個接口

//定義一個接口
interface SimpleInter {
    fun test()
    val number: Int
}
複製代碼

定義一個類

//open表示容許繼承,在Kotlin中,類和方法之間默認不容許繼承和重寫(不包括抽象類)
open class SimpleClass{
		open fun put()
}
複製代碼

類之間的繼承及實現一個接口

//實現接口中的參數
class Test1(override val number: Int) :SimpleClass(),SimpleInter{
    
    //接口方法
    override fun test() {
    
    }
    
    //重寫父類方法
    override fun openTest() {
        super.openTest()
    }

}
複製代碼

自定義set,get

//在kotlin中,默認會爲參數添加set,get方法,若是須要自定義,按照如下方式寫便可
class ClassTest1(override val number: Int) : SimpleInter {
    override fun test() {

    }

    var money = number
        get() {
            return field
        }
        set(value) {
            field = value
        }

}
複製代碼

類的實例化及屬性的調用

val number=Test1::number

//屬性調用
val classTest=ClassTest1(123)
val money=classTest::money
money.set(456)
複製代碼

擴展方法

class Book{

}

//定義Book類的擴展方法
fun Book.noBook(){
	
}

//也能夠定義擴展屬性
// Backing Field
//擴展成員變量沒法存儲狀態,由於不存在field
var Book.money: Int
    get() {
        return this.saveInt
    }
    set(value) {
        this.saveInt = value
    }

//接口能夠定義變量
interface Guy {
    var moneyLeft: Double
        get() {
            return 0.0
        }
        set(value) {

        }
}
複製代碼

空類型安全

class Book{

}

fun main() {
    val book=Book()
    if (book is Book){
        //安全的類型轉換
        println((book as? Book)?.javaClass?.name)
    }
}
複製代碼

Kotlin-內置類型

基本類型

image-20191228150348866

var b: String = "asdasd"
val c: Int = 15
複製代碼

字符串比較,

==,===

== 比較內容是否相等

=== 比較對象是否相等

數組

image-20191228155051782

//it表明當前數組下標
var intArray = IntArray(3) { it +3}

//獲取長度
println(intArray.size)
//打印數組
println(intArray.contentToString())
//打印數組
val dd= arrayOf(1,2,3)
println("${dd[1]},${dd[2]}")
//按數組下標打印
val ss= intArrayOf(1,2,3,4);
    for (i in ss.indices){
        println(ss[i])
}

//數組遍歷
for (d in dd){
        print(d)
}
//函數式寫法
 dd.forEach { d->
        println(d)
 }
//若是使用默認it,則能夠省略 d->
dd.forEach { println(it)}


/* 判斷是否在一個數組內 */
   if (5 in dd){
        println("在數組dd內")
    }

    if (5 !in dd){
        println("不在數組dd內")
    }


複製代碼

區間

//表示建立了一個1-10的閉區間
val intRange=1..10
val charRange='a'..'z'
val longRange=1L..1000L

//建立開區間
val intRangeExclusive=1 until  10 //[1,10)

//倒序區間
    val intRangeReverse=10 downTo  1 //[10,9,...,1]

//區間添加步長
val intRange1=1..10 step 3 // [1,4,7,9,10]
 //打印區間
println(intRange1.joinToString())

複製代碼

集合框架

 image-20191228170007121

//不可變List,不可添加或刪除
val intList: List<Int> = listOf(1,2,3)
//可變List
val intList2: MutableList<Int> = mutableListOf(1, 2, 3, 4)

//不可變map 
val map:Map<String,Any> = mapOf("name" to "petterp","ts" to 123)
val mutilMap:Map<String,Any> = mutableMapOf("name" to "petterp","Ts" to 123)


//直接建立Java中的List
val intList = ArrayList<Int>()
val stringList = ArrayList<String>()

val intList = ArrayList<Int>()
    //List的寫入 等價於 add
    for (i in 0..10){
        intList+=i
    }
    //刪除某個元素,等價於 remove
    intList.remove(100)
    //修改某個元素的值 相似於 set(0,101)
    intList[0]=101
    

val maps = HashMap<String, String>(10)
    //添加某個元素
    maps+=("key1" to "test")
    println(maps.toString())
    //修改某個元素
    maps["key1"]="petterp"
    println(maps.toString())

複製代碼

Any 至關於Object

若是咱們查看 Kotlin中的 ArrayList,會發現

image-20191228180401420

其中 Typealias 至關與爲類型起新名稱

咱們在上面Map中用到了 」「 to ""

Var pair=""

val map = HashMap<String, String>()

    val pair = "key" to "petterp"
    val pair2 = Pair("Hello", "Petterp")
    //獲取鍵
    val key = pair.first
    //獲取值
    val second = pair.second
    //puts如map
    map += pair
    map += pair2
    println("$key , $second")
複製代碼

什麼是解構?

用於將一個對象解構成多個變量

使用案例以下

fun main() {

    val map = HashMap<String, Int>()
    val kotlinBook = "Kotlin核心技術" to 88
    val AndroidBook = Pair("Android藝術探索", 66)
    println("${kotlinBook.first} , ${kotlinBook.second}")
    map += kotlinBook
    map += AndroidBook println("解構開始\n\n") /*解構聲明*/ //參考https://www.kotlincn.net/docs/reference/multi-declarations.html //將一個對象解構爲多個變量 println("解構對象") val (x, y) = Book("Kotlin核心技術", 88)
    println(x)
    println(y)


    println("\n 遍歷map")
    //使用解構遍歷map,多是最好的方式
    for ((name, value) in map) {
        println("$name , $value")
    }

    println("\n 忽略某些解構")
    //若是你不想解構某些變量,則經過 _ 標誌
    for ((name, _) in map)
        println(name)

    println("\n lambda中使用解構")
    //使用解構返回一個新的map,key不變,返回的只是value的改變
    val maps = map.mapValues { (name, value) -> "5" }
    println(map.toString())
    println(maps.toString())

    println("\n 函數中使用解構")
    //從函數中返回兩個變量
    val (name, money) = funcation()
    println("$name , $money")
    //從函數中返回兩個變量
}

data class Book(var name: String, var money: Int) fun funcation(): no1.Book {
    println("經歷了一波操做")
    return no1.Book("Android藝術探索", 99)
}

複製代碼
Kotlin核心技術 , 88
解構開始


解構對象
Kotlin核心技術
88

 遍歷map
Kotlin核心技術 , 88
Android藝術探索 , 66

 忽略某些解構
Kotlin核心技術
Android藝術探索

 lambda中使用解構
{Kotlin核心技術=88, Android藝術探索=66}
{Kotlin核心技術=5, Android藝術探索=5}

 函數中使用解構
經歷了一波操做
Android藝術探索 , 99

複製代碼

Nothing

在你的判斷邏輯中,充當永遠不可能調用的哪一項,好比你有一個when的選擇語句,就可使用Nothing做爲你的else返回。

先看一下Nothing是什麼

/** * Nothing has no instances. You can use Nothing to represent "a value that never exists": for example, * if a function has the return type of Nothing, it means that it never returns (always throws an exception). */
//*沒有實例。您可使用Nothing來表示「一個永不存在的值」:例如,*若是函數的返回類型爲Nothing,則表示該函數永不返回(老是引起異常)。
public class Nothing private constructor()
複製代碼

示例以下:

有下面這樣一段代碼:

fun test(type: String) =
    when (type) {
        "key1" -> "條件1"
        "key2" -> "條件2"
        else -> caseDefault()
    }

/** 一個報錯的邏輯,爲了便於編譯器識別,返回類型依然爲String * 若是是其餘狀況,相應返回類型須要更改方可便於編譯器,不然相應的異常每一個方法都須要手動添加 */
private fun caseDefault(): String {
    throw RuntimeException("123")
}
複製代碼

使用 Nothing 優化這段代碼

fun test(type: String) =
        when (type) {
            "key1" -> "條件1"
            "key2" -> "條件2"
            else -> doNothing()
        }

private fun doNothing(message: String="Nothing is all"): Nothing {
    throw RuntimeException(message)
}
複製代碼

Kotlin函數

:: 函數引用

fun main() {
    //函數引用的幾種方法
    val x = Test::pp
    val x1: (Test, String) -> String = Test::pp
    val x2: (Test, String) -> String = x
    val x3: Function2<Test, String, String> = Test::pp
}


class Test {
    fun pp(name: String): String {
        return name
    }
   
}
複製代碼

函數做爲參數使用

fun main(){
  val x1: (Test, String) -> String = Test::pp
println(Test().pp2(x1, 123))
}

class Test(){
  
  fun pp(name: String): String {
        return name
    }
  
  fun pp2(name: (Test, String) -> String, value: Int): String {
        return "${name(Test(), "132")} , $value"
    }
}
複製代碼

默認參數

fun main(){
    //默認參數
    default("Android")
    //若是你的默認參數爲第一個,此時就須要聲明顯示類型
    default(name = "Android")
}

fun default(name: String, money: Int = 9) {

}
複製代碼

變長參數

fun main(){
	 //變長參數
    channels("Asd", "Asd")
}
fun channels(vararg args: String) {
    println(args.joinToString())
}
複製代碼

多返回值參數

fun main(){
var (a, b, c) = test1()
    println("$a , $b ,$c")
}

fun test1(): Triple<Int, String, String> {
    return Triple(123, "asd", "asdfa");
}
複製代碼

簡易計算器

fun main() {
    val res: String = readLine()!!
    if (res.isEmpty()) {
        throw RuntimeException("不可爲null")
    }

    val operations = mapOf(
        "+" to ::plus,
        "-" to ::minus,
        "*" to ::times,
        "/" to ::div
    )

    when {
        "+" in res -> {
            val (a, b) = res.split("+")
            println(operations["+"]?.invoke(a.toInt(), b.toInt()))
        }
        "-" in res -> {

        }

        "*" in res -> {

        }
        "/" in res -> {

        }
    }
}

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

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

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

fun div(a: Int, b: Int): Int {
    return a + b
}
複製代碼

Kotlin-類型再進階

類的構造器

fun main() {
    Kang::name
    Kang::money //報錯
}

//加了var或者val 就至關於成員變量,全局可見,不然就只能在構造器(init塊)內可見,相似於局部變量
class Kang(var name:String,money:Int)
複製代碼

init塊

init會在最後合成爲一個init塊,會與構造方法一塊兒執行

class Kang(var name:String,money:Int){
    init {
        this.name=money.toString()
    }

    init {
        println()
    }
    
    init {
        println("我是Petterp")
    }
}
複製代碼

副構造器

在Java裏面,一個類能夠有多個構造器,也就意味着咱們初始化這個類的方法有不少種,這也就意味着咱們有不少生命了的變量沒法被使用到。

class Kang(override var name:String, money:Int):Test(name){
    init {
        this.name=money.toString()
    }

    init {
        println()
    }

    init {
        println("我是Petterp")
    }
    fun void(){
    }


    //副構造器,同時調用了主構造器
    constructor(name: String):this(name,123){
        this.name=name
    }
}  

複製代碼

若是未定義主構造器,則能夠直接調用其餘副構造器,和java基本沒有什麼不一樣.不過這種寫法並不推薦

class Test3{
    constructor(name:String){
        
    }
    
    constructor(money:Int):this("123"){
        
    }
}
複製代碼

Kotlin推薦以主構造器+默認參數的形式去寫。若是要用在Java中,則加上 @JvmOverloads

class Test3 constructor(){
    constructor(name:String){

    }

    @JvmOverloads
    constructor(money:Int):this("123"){

    }
}
複製代碼

類與成員的可見性

可見性類型 Java Kotlin
public 公開 與java相同,默認就是public
internal x 模塊內可見
default 包內可見,默認 x
protected 包內及子類可見 類內及子類可見
private 類內可見 類或文件內可見

internal的小技巧

在Java中能夠直接訪問到 internal ,由於Java並不認識Kotlin中的 internal。以下,兩個模塊中

Kotlin:

image-20191231173335878

Java:

image-20191231173432841

若是咱們想避免Java直接訪問到咱們的代碼,能夠加入如下小技巧,這樣當Java調用時就會因不規範而報錯。

image-20191231173528846

image-20191231173552689

私有屬性的 set,get權限

//
class Person(var age: Int, var name: String) {
     var fitstName: String = ""
        private set(value) {
            field = name
        }
}
複製代碼

頂級聲明的可見性

  • 頂級聲明指文件內直接定義的屬性,函數,類等

  • 頂級聲明不支持 protected

  • 頂級聲明被 private 修飾表示文件內部可見

延遲初始化

  • 類屬性必須在構造時初始化
  • 某些成員只有在類構造以後纔會被初始化
  1. latteinit 會讓編譯器忽略變量的初始化,不支持Int等基本類型
  2. 開發者必須可以徹底肯定變量值的生命週期下使用 Iateinit

有以下三種方式:

lazy,推薦方式

private val rvMainActivity by lazy {
        findViewById<RecyclerView>(R.id.rv_main)
}
複製代碼

lateinit,使用時需注意null處理

private lateinit var recyclerView:RecyclerView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        recyclerView=findViewById(R.id.rv_main)
    }
複製代碼

?,不推薦,每次都須要?或者!!

private var recyclerView:RecyclerView?=null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        recyclerView=findViewById(R.id.rv_main)
        recyclerView?.adapter=RecyclerCustomAdapter(arrayListOf("123","234"))
    }
複製代碼

代理

Delegates.observable

用於監控屬性值發生變動,相似一個觀察者,當屬性值被修改後往外部拋出一個變動的回調。

//使用state變量代理StateManager,從而屬性更改時實現監聽
class StateManager{
    var state:Int by Delegates.observable(0){
        property, oldValue, newValue ->
        println("$oldValue->$newValue")
    }
}


fun main() {
    var stateManager = StateManager()
    stateManager.state=4
    stateManager.state=5
}
複製代碼

屬性代理

By

fun main(){
  //屬性x將它的訪問器邏輯委託給了X對象
    var x:Int by X() 
}   

    
//遵循getValue,setValue,若是是val,只支持getValue
class X{
    operator fun getValue(nothing: Nothing?, property: KProperty<*>): Int {
            return   2
    }

    operator fun setValue(nothing: Nothing?, property: KProperty<*>, i: Int) {


    }
}
複製代碼

Delegates.notNull()

須要在使用前初始化

//返回具備 非Null參數的委託
 var name:String by Delegates.notNull()
複製代碼

Delegates.vetoble

用於在屬性改變發生是通知外部變化,在裏面能夠作一些改變,返回一個狀態,若是知足條件返回true,不然false

var vetoable: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
        println("old-$oldValue---new-$newValue")
        return@vetoable newValue == 123
    }
複製代碼

Delegates.observable

用於在屬性發生變化時調用指定的回調函數

var observable:Int by Delegates.observable(0){
        property, oldValue, newValue -> 
        
    }
複製代碼

使用屬性代理讀寫 Propertie

class PropertiesDelegate(private val path: String, private val defaultValu: String = "") {
    private lateinit var url: URL

    private val properties: Properties by lazy {
        val prop = Properties()
        url = try {
            javaClass.getResourceAsStream(path).use {
                prop.load(it)
            }
            javaClass.getResource(path)!!
        } catch (e: Exception) {
            try {
                ClassLoader.getSystemClassLoader().getResourceAsStream(path).use {
                    prop.load(it)
                }
                ClassLoader.getSystemClassLoader().getResource(path)!!
            } catch (e: Exception) {
                FileInputStream(path).use {
                    prop.load(it)
                }
                URL("file://${File(path).canonicalPath}")
            }
        }
        prop
    }

    operator fun getValue(thisRef: Any?, kProperty: KProperty<*>): String {
        return properties.getProperty(kProperty.name, defaultValu)
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        properties.setProperty(property.name, value)
        File(url.toURI()).outputStream().use {
            properties.store(it, "petterp")
        }
    }
}

abstract class AbsProperties(path: String) {
    protected val prop = PropertiesDelegate(path)
}

class Config : AbsProperties("Config.properties") {
    var name by prop
}

fun main() {
    val config=Config()
    println(config.name)
    config.name="asdasd"
    println(config.name)
}
複製代碼

單例 Object

//餓漢式單例
object Singleton{

}
複製代碼
便於Java調用
@JvmStatic 生成靜態方法
@JvmField 生成set,get
複製代碼

伴生對象

Kotlin中沒有static變量,因此使用伴生對象代替靜態變量。使用前須要帶上 相應註解

class Test{

    companion object{
        fun of(){
            
        }
    }
}
複製代碼

內部類

class Outher{
    //非靜態內部類
    inner class Inner{
        
    }
    
    //靜態內部類
    class staticInner{
        
    }
}
複製代碼

內部Object

class Outher {
    //非靜態內部類
    inner class Inner {

    }

    //靜態內部類
    class staticInner {
        object OutherObject {
            var x=5
        }
    }
}


fun main() {
    Outher.staticInner.OutherObject.x
}
複製代碼

匿名內部類

fun main() {
  	//可繼承父類或實現多個接口
  	//類型-交叉類型 ClassPath&Runnable(猜想)
    object : ClassPath(), Runnable {
        override fun run() {
        }

        override fun test() {
        }
    }
}

abstract class ClassPath {
    abstract fun test()
}
複製代碼

數據類

Data

data class Book(val id:Int,val money:Double){
    
}
複製代碼

與JavaBean的區別

JavaBean data class
構造方法 默認無參構造 屬性做爲參數
字段 字段私有,Getter/Setter公開 屬性
繼承性 可繼承也可被繼承 不可被繼承
Component 相等性,解構等

如何合理使用data class

  • 使用基本類型做爲屬性參數

枚舉類

enum class State{
    A,B
}

enum class States(val id:Int){
    Idle(0),Busy(1)
}

//枚舉實現接口
enum class ClassPath : Runnable {
    IDLE,BUSY;

    override fun run() {
    }
}

//爲每個枚舉實現接口方法
enum class EnumOffer : Runnable {
    idle {
        override fun run() {
        }
    },
    busy{
        override fun run() {
        } 
    }
    override fun run() {
    }
   
}
複製代碼

爲枚舉定義擴展

fun State.sout() {
    println("擴展")
}
複製代碼

枚舉的比較

enum class State {
    A, B
}

fun main() {
    val s = State.B
    if (s>State.A) println("ok")
}
複製代碼

枚舉的區間

enum class State {
    A, B,C,D,E
}
fun main() {
    val s = State.B
    if (s in State.A..State.C) println("ok")
}
複製代碼

密封類

  • 密封類是一種特殊的抽象類
  • 密封類的子類定義在自身相同的文件中
  • 密封類的子類個數有限

簡單來講,密封類至關於一類事物的具體子分類,有明確的類型區別,子類有具體個數。在when表達式中,對密封類有優化效果

sealed class Book(val name: String)

class Android(name: String) : Book(name)
class Java(name: String, var money: Int) : Book(name)

fun main() {
    list(Android("Kotlin"))
    list(Java("Javaxx", 99))
}

//在條件所有知足的狀況下,無需else
fun list(book: Book) = when (book) {
    is Android -> println(book.name)
    is Java -> println("${book.name} -- ${book.money}")
}
複製代碼

密封類與枚舉類的區別

密封類 枚舉類
狀態實現 子類繼承 類實例化
狀態可數 子類可數 實例可數
狀態差別 類型差別 值差別

內聯類 inline class

  • 內聯類是對某一個類型的包裝
  • 內聯類是相似於 Java 裝箱類型的一種類型
  • 編譯器會盡量使用被包裝的類型進行優化

內聯類使用場景

無符號類型
inline class Unit constructor(internal val data: Int) : Comparable<Unit> {
    override fun compareTo(other: Unit): Int {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

}
複製代碼
相關文章
相關標籤/搜索