Kotlin的解析(拓展)

前言

  在前幾篇的基礎上,你們若是認真的閱讀,並跟着思路實踐的話,應該能夠收穫不少的,前面基本已經覆蓋了Kotlin語言中常見的使用方法,下面讓咱們來進一步,在前面的基礎上深深的擴展一下java

1. Kotlin的技術拓展其一

  儘管到目前爲止,咱們已經講了不少關於Kotlin的新技術,但遠遠是不夠的,讓咱們進一步瞭解更多的Kotlin的新知識編程

1.1 數據結構與集合

1.1.1 數據結構

  所謂的數據結構,就是將對象中的數據解析成相應的獨立變量,也就是脫離原來的對象存在安全

data class Person(var name:String, var age :Int,var salary:Float)

var person = Person("Bill",30,120f)
var (name,age,salary)=person //數據解構
Log.i("tag",name+age+salary)
輸出
Bill20120
複製代碼

  有不少的對象,能夠保存一組值,並能夠經過for...in的語句,解構出值bash

var map = mutableMapOf<Int,String>()
        map.put(10,"Devin")
        map.put(20,"Max")

        for ((key,values) in map){
            Log.d("tag",key.toString() +";;;;"+values)
        }

輸出
    10;;;;Devin
    20;;;;Max
//其中這些對象都是經過數據類實現的,固然咱們本身也能夠實現的,這裏就不作展現了,本身能夠下去試試
複製代碼
1.1.2 集合

  儘管Kotlin可使用JDK中提供的集合,但Kotlin標準庫也提供了本身的集合,與之不一樣的是,Kotlin提供的集合分爲可修改和不可修改的,這一點和Apple的CocoaTouch相似。在Kotlin只讀包括LIst、Set、Map;可寫的包括MutableList、MutableSet、MutableMap等數據結構

public interface List<out E> : Collection<E> {
...
}

public interface Set<out E> : Collection<E> {
...
}

public interface Map<K, out V> {
...
}

很顯然上面的都是out修飾的,前面學的out聲明,泛型若是使用了,那麼該泛型就能只用於讀操做

val nums = mutableListOf<Int>(1,2,3)
        var reNums :List<Int> = nums;
        nums.add(4)//能夠增長;reNums只能讀取
複製代碼

  從這個代碼能夠看出,集合並無提供構造器建立集合對象,提供了一些函數來建立編程語言

listOf; setOf; mapOf; mutableListOf; mutableSetOf; mutableMapOf函數

val nums = mutableListOf<Int>(1,2,3)
var toList = nums.toList()//經過此方法能夠把讀寫的專爲只讀的
 var toMutableList = toList.toMutableList()//只讀的也能夠轉爲讀寫的
複製代碼

1.2 範圍值

1.2.1 值範圍的應用

  值範圍表達式用rangTo函數實現,該函數的操做形式是(..),相關的操做符in和!in工具

var n =20
        if(n in 1..10){
            Log.d("tag","知足條件")
        }
        
        if (n !in 30..80){
         Log.d("tag","知足條件")   
        }
複製代碼

  整數的值範圍(IntRange、LongRange、CharRange)還有一種額外的功能,就是能夠對這些值範圍進行遍歷。編譯器會負責將這些代碼轉換爲Java中基於下標的for循環,不會產生沒必要要的性能損耗性能

for(i in 1..10){
Log.i("tag",i.tostring())
}
//至關於Java中的
//for(int i=1; I<=10;i++)

for(i in 10..1){
//若是按照倒序的話,什麼都不會輸出的
Log.i("tag",i.tostring())
}

//可是非要按照倒序輸出,只要使用標準庫中的downTo函數就能夠了

for(i in 10 downTo 1){
Log.i("tag",i*i) //輸出100到1共10個數
}

//在前面的代碼中,i的順序加1或減1,也就步長爲1;若是要是改變步長的話,可使用step函數
for(i in 1..10 step 2){
   Log.i("tag",i.toString())
}
輸出:1,3,5,7,9


//在前面的代碼,使用的範圍都是閉區間,要是這種的形式[1,10)

for(i in 1 until 10){
//不包含10的
     Log.i("tag",i.toString())
 }

複製代碼
1.2.2 經常使用工具函數

(1)rangTo,整數類型上定義的rangTo操做符,只是簡單地調用*Rang類的構造函數ui

class Int
{
 public operator fun rangeTo(other: Int): IntRange
 public operator fun rangeTo(other: Long): LongRange
}


複製代碼

(2)downTo,擴展函數能夠用於一對整數類型,下面就是經過擴展函數添加的downTo函數

public infix fun Long.downTo(to: Long): LongProgression {
    return LongProgression.fromClosedRange(this, to, -1L)
}

public infix fun Byte.downTo(to: Long): LongProgression {
    return LongProgression.fromClosedRange(this.toLong(), to, -1L)
}
複製代碼

(3)reversed,對於每一個*Progression類都定義了reversed擴展函數,全部的這些函數都會返回相反的數列

public fun IntProgression.reversed(): IntProgression {
    return IntProgression.fromClosedRange(last, first, -step)
}
複製代碼

(4)對於每一個*Progression類都定義了step擴展函數,全部這些函數都會返回使用新的step值,步長值參數要求永遠是整數,所以這個函數不會改變數列遍歷的方向

public infix fun IntProgression.step(step: Int): IntProgression {
  if (!isPositive) throw IllegalArgumentException("Step must be positive, was: $step.")
   return IntProgression.fromClosedRange(first, last, if (this.step > 0) step else -step)
}
複製代碼

注意:函數返回的數列last值可能與原始數列的last的值不一樣,這是爲了保證(last-first)%increment==0原則

1.3 類型檢查與類型轉換

1.3.1 is 與 !is操做符
var obj: Any = 234
       
        if (obj is String) {
            
        }
        
        if (obj is Int){
            
        }
        
        if (obj !is Int){
            
        }
複製代碼

  若是is表達式知足條件,Kotlin編譯器會自動轉換is前面的對象到後面的數據類型

var obj: Any = 234
        if (obj is Int){
            obj.rangeTo(4)//Int類型纔有的,自動轉換了
        }

 //注意的是,對象is後面類型要兼容,若是不兼容的話,沒法編譯經過
  var obj = 234
        if (obj is String) {//編譯不過
            obj.rangeTo(4)
        }
複製代碼
1.3.2 智能類型轉換
var a :Any = "max"
        //&&的右側已經轉換成了string
        if (a is String && a.length>0){
            
        }
        // ||的右側也已經轉換爲string
        if (a !is String ||a.length<0){
            
        }

//這種類型的轉換對於when和while一樣有效果的

 var x :Any ="sfs"
      
        when(x){
            is Int -> Log.i("tag", (x+1).toString())
            is String ->  Log.i("tag", x.length.toString())
        }
複製代碼
1.3.3 強行類型轉換

  若是類型強制轉換,並且類型不兼容,類型轉換操做符一般會拋出一個異常,稱之爲不安全的,而不安全的類型轉換使用中綴操做符as

var a :Any ="max"
        val x :Int = a as Int  //java.lang.ClassCastException


//爲了不拋出異常,咱們可使用安全的類型轉換操做符 as?,當類型轉換失敗時,它會返回null

 var a :Any? =null
        val x : Int? = a as Int?  
        Log.i("tag", x.toString())// null


var a :Any? ="max"
        val x : Int? = a as? Int? //as後面也要加?否則仍是會拋異常
        Log.i("tag", x.toString())// null

複製代碼
1.3.4 this表達式

  爲了訪問外層範圍內的this,咱們使用this@lable,其中@lable是一個標籤,表明this所屬範圍

class  A{
        var A =13
        inner  class  B{
            fun Int.foo(){
                val a =this@A //指向A的this
                val b = this@B //指向B的this
                val c =this//指向foo()函數接收者,一個Int值
                
                val d =this@foo //指向foo()函數接收者,一個Int值
                
                val funLit = {
                    s:String -> 
                    val e = this//指向foo()函數接收者,由於包含當前代碼的Lambda表達式沒有接收者
                }
            }
        }
    }
複製代碼

2. Kotlin的技術拓展其二

  本章將會繼續探索null值安全性、異常類、註解以及反射 #####2.1 null值安全性   在Java中,常常遇到空指針的困擾,表腦瓜子疼,對於這個Kotlin使用一些新的語法糖,會盡量避免null異常帶來的麻煩

2.1.1 可爲null與不可爲null類型
var a:String =null  //編譯錯誤,不能爲null
var b:String = "abc"
b=null   //編譯錯誤,不能爲null
複製代碼

  要容許null值,咱們能夠將變量聲明爲null的字符串類型:String ?

var a :String ="abcd"
var b:String? = "abc"
b =null

var len = a.length    //因爲a不容許爲null,所以不會產生NPE

val len1 = b.length //編譯出錯,由於b可能爲null

//要是必須訪問的話,使用if語句進行判斷
var len = if (b==null) -1 else b.length;

//第二種就是使用安全調用操做符:?

print(b?.length) //輸出爲null

//固然可使用在類中的調用

bob?.depart?.head?.name

//這樣的鏈式調用,只有屬性鏈中任何一個屬性爲null,整個表達式就會返回null

複製代碼
2.1.3 Elvis操做符

  假設咱們有一個可爲null的引用r,咱們能夠認爲:若是不爲空,就是用,不然使用其餘的值

//若是"?:"左側的表達式不是null,Elvis操做符就會返回它的值,不然,返回右側表達式的值,注意,只有在左側表達式爲null,纔會計算右側表達式的值
        var len1 = b?.length ?:-1
複製代碼

     在Kotlin中,因爲throw和return都是表達式,所以能夠用在右側

var len1 = b?.length ?:throw NullPointerException()
        var len2 = b?.length ?:return 

複製代碼
2.1.4 !!操做符

對於NPE的忠實粉絲,還能夠寫!!b,對於b不爲null的狀況,這個表達式會返回一個非null的值,若是是null,就會拋出NPE

var len2 = b!!.length
複製代碼

#####2.2 異常類   Kotlin中全部的異常類都是Throwable的子類,要拋出異常,可使用throw表達式

//和Java的使用區別不是太大,這裏就不說了
try { } 
catch (e: NullPointerException) {
           null
        } 
finally {
        }
複製代碼
2.3 註解(Annotations)

  註解是用來爲代碼添加元數據(metadata)的一種手段,要聲明一個註解,須要在類以前添加annotation修飾符

annotation class Fancy

  註解的其餘屬性,能夠經過向註解類添加元註解(meta-annotation)的方式指定 (1)@Target 指定這個註解可被用於哪些元素(類、函數、屬性和表達式) (2)@Retention指定這個註解的信息是否被保存到編譯後class文件中,以及在運行時是否能夠經過反射訪問到它(默認狀況下,這兩個設定都是true) (3)@Repetable容許在單個元素上屢次使用同一註解 (4)@MustBeDoucumented表示這個註解是公開API的一部分,在自動產生的API文檔的類或者函數簽名中,應該包含這個註解的信息

@Target(AnnotationTarget.CLASS ,AnnotationTarget.FUNCTION)
    @Retention(AnnotationRetention.SOURCE)
    @MustBeDocumented
    @Repeatable
    annotation class MyAnnotationClass{
        
    }
複製代碼
2.3.1 使用註解

  註解能夠在類、函數、函數參數和函數返回值中使用

@ MyAnnotationClass
class Foo {
@ MyAnnotationClass fun bazz(@MyAnnotationClass foo : Int):Int{
return (@MyAnnotationClass l)
}
}

//若是須要對一個類的主構造器加註解,那麼必須在主構造器聲明中添加constructor關鍵字,而後在這個關鍵字以前添加註解

class Foo @MyAnnotationClass constructor(n:Int){
// ...
}
複製代碼
2.3.2 註解類的構造器

註解類能夠擁有帶參數的構造器

annotation class Special(val why :String)

使用Special("example") class Foo{}

並非全部類型的參數都容許在註解類的構造器中使用,註解構造器只容許使用下面類型的參數 (1)與Java基本類型對應的數據類型(Int、Long) (2)String (3)枚舉類 (4)KClass (5)其餘註解類

2.4 反射(Reflection)

  儘管Kotlin是基於JVM的編程語言,但在Kotlin中使用反射,須要引用額外的庫(kotlin-reflect.jar)

2.4.1 類引用

val c = MyClass::class 類引用是一個KClass類型的值

  注意,Kotlin的類引用不是一個Java的類引用,要獲得Java的類引用,可以使用KClass對象實例的java屬性

val c =MyClass::class.java

2.4.2 枚舉類成員

  反射最經常使用的功能之一就是枚舉的成員,如類的屬性、方法等。

class Person(val name :String,val num :Int){
    fun process(){
    }
}

var c = Person :: class

// 獲取Person類中全部的成員列表(屬性和函數)
println("成員數:" +c.members.size)
for(member in c.members){
    //  輸出每一個成員的名字和返回類型
    print(member.name +"" +member.returnType)
    println()
}
//獲取Person類中全部屬性的個數
println("屬性個數:" +c.memberProperties.size)
//枚舉Person類中全部的屬性
for(property in c.memberProperties){
    //輸出當前屬性的名字和返回類型
    print(property.name+""+property.returnType)
    println()
}
//獲取Person類中全部函數的個數
println("函數個數:"+c.memberFunctions.size)
for(function in c.memberFunctions){
//輸出當前函數的名字和返回類型
    println(function.name+" " +function.returnType)
}

執行這段代碼,會輸出以下內容

成員數:6
num kotlin.Int
value kotlin.String
process kotlin.Unit
equals kotlin.Boolean
hashCode kotlin.Int
toString kotlin.String
屬性個數:2
num kotlin.Int
value kotlin.String
函數個數:4
process kotlin.Unit
equals kotlin.Boolean
hashCode kotlin.Int
toString kotlin.String
複製代碼
2.4.3 動態調用成員函數

  反射的另一個重要應用就是能夠動態調用對象的成員,如成員函數、成員函數、成員屬性,所謂的動態調用,就是根據成員名字進行調用,能夠動態指定成員的名字,經過::操做符,能夠直接返回類的成員

class Person(val name:String ,val num:Int){
      fun process(){
        println("name:${value} num:${num}")
    }
}

// 獲取process函數對象
var p = Person::process
// 調用invoke函數執行process函數
p.invoke(person("abc",20))
//利用Java的反射機制指定process方法名字
var method  = Person::class.java.getMethod("process")
//動態調用process函數
method.invoke(Person("Bill",30))


輸出:
name : abc  num: 20
value : Bill    num: 30

複製代碼
2.4.4 動態調用成員屬性

  Kotlin類的屬性與函數同樣,也可使用反射動態調用,不過Kotlin編譯器在處理Kotlin類屬性時,會將器轉換爲getter和setter方法,而不是與屬性同名的Java字段。

class Person
{
var name :String = "Devin"
                 get() = field
                 set(v){
                     field = v
                     }
}

複製代碼

  很明顯,name屬性變成了getName和setName方法,所以,在使用反射技術訪問Kotlin屬性時,仍然需按成員函數處理,若是使用Java的反射技術,仍然要使用getMethod方法獲取getter和setter方法對象,而不能使用getField方法獲取字段

class Person
{
var name :String = "Devin"
                 get() = field
                 set(v){
                     field = v
                     }
}

var person  = Person()
//  得到屬性對象
var name = Person::name
// 讀取屬性值
println(name.get(person))
// 設置屬性值
name.set(person,"Mike")
println(name.get(person))

//沒法使用getField方法得到name字段值,由於根本就沒生成name字段,只有getName和setName方法
var field = Person::class.java.getField("name")
field.set(person,"Json")
println(field.get(person))

//利用Java反射獲取getName方法
var getName = Person::class.java.getMethod("getName")
//利用 Java反射獲取SetName方法,注意,getMethod方法的第2個參數可變的
//須要傳遞setName參數類型的class
//這裏不能指定Kotlin中的String,而要指定java.lang.String

var setName = Person::class.java.getMethod("setName",java.lang.String().javaClass)
//動態設置name屬性的值
setName.invoke(person,"John")
//動態獲取name屬性的值
println(getName.invoke(person))

複製代碼

總結

  經過這一篇,更深刻的瞭解kotlina的更多新的知識,以及語法糖,和Java區別仍是比較大的,這篇講的,實際開發中非常有用的,可能將的仍是有限的,畢竟一些知識,還得在實踐的更深刻的掌握

相關文章
相關標籤/搜索