在前幾篇的基礎上,你們若是認真的閱讀,並跟着思路實踐的話,應該能夠收穫不少的,前面基本已經覆蓋了Kotlin語言中常見的使用方法,下面讓咱們來進一步,在前面的基礎上深深的擴展一下java
儘管到目前爲止,咱們已經講了不少關於Kotlin的新技術,但遠遠是不夠的,讓咱們進一步瞭解更多的Kotlin的新知識編程
所謂的數據結構,就是將對象中的數據解析成相應的獨立變量,也就是脫離原來的對象存在安全
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
//其中這些對象都是經過數據類實現的,固然咱們本身也能夠實現的,這裏就不作展現了,本身能夠下去試試
複製代碼
儘管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()//只讀的也能夠轉爲讀寫的
複製代碼
值範圍表達式用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)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原則
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)
}
複製代碼
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())
}
複製代碼
若是類型強制轉換,並且類型不兼容,類型轉換操做符一般會拋出一個異常,稱之爲不安全的,而不安全的類型轉換使用中綴操做符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
複製代碼
爲了訪問外層範圍內的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表達式沒有接收者
}
}
}
}
複製代碼
本章將會繼續探索null值安全性、異常類、註解以及反射 #####2.1 null值安全性 在Java中,常常遇到空指針的困擾,表腦瓜子疼,對於這個Kotlin使用一些新的語法糖,會盡量避免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
複製代碼
假設咱們有一個可爲null的引用r,咱們能夠認爲:若是不爲空,就是用,不然使用其餘的值
//若是"?:"左側的表達式不是null,Elvis操做符就會返回它的值,不然,返回右側表達式的值,注意,只有在左側表達式爲null,纔會計算右側表達式的值
var len1 = b?.length ?:-1
複製代碼
在Kotlin中,因爲throw和return都是表達式,所以能夠用在右側
var len1 = b?.length ?:throw NullPointerException()
var len2 = b?.length ?:return
複製代碼
對於NPE的忠實粉絲,還能夠寫!!b,對於b不爲null的狀況,這個表達式會返回一個非null的值,若是是null,就會拋出NPE
var len2 = b!!.length
複製代碼
#####2.2 異常類 Kotlin中全部的異常類都是Throwable的子類,要拋出異常,可使用throw表達式
//和Java的使用區別不是太大,這裏就不說了
try { }
catch (e: NullPointerException) {
null
}
finally {
}
複製代碼
註解是用來爲代碼添加元數據(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{
}
複製代碼
註解能夠在類、函數、函數參數和函數返回值中使用
@ MyAnnotationClass
class Foo {
@ MyAnnotationClass fun bazz(@MyAnnotationClass foo : Int):Int{
return (@MyAnnotationClass l)
}
}
//若是須要對一個類的主構造器加註解,那麼必須在主構造器聲明中添加constructor關鍵字,而後在這個關鍵字以前添加註解
class Foo @MyAnnotationClass constructor(n:Int){
// ...
}
複製代碼
註解類能夠擁有帶參數的構造器
annotation class Special(val why :String)
使用Special("example") class Foo{}
並非全部類型的參數都容許在註解類的構造器中使用,註解構造器只容許使用下面類型的參數 (1)與Java基本類型對應的數據類型(Int、Long) (2)String (3)枚舉類 (4)KClass (5)其餘註解類
儘管Kotlin是基於JVM的編程語言,但在Kotlin中使用反射,須要引用額外的庫(kotlin-reflect.jar)
val c = MyClass::class 類引用是一個KClass類型的值
注意,Kotlin的類引用不是一個Java的類引用,要獲得Java的類引用,可以使用KClass對象實例的java屬性
val c =MyClass::class.java
反射最經常使用的功能之一就是枚舉的成員,如類的屬性、方法等。
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
複製代碼
反射的另一個重要應用就是能夠動態調用對象的成員,如成員函數、成員函數、成員屬性,所謂的動態調用,就是根據成員名字進行調用,能夠動態指定成員的名字,經過::操做符,能夠直接返回類的成員
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
複製代碼
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區別仍是比較大的,這篇講的,實際開發中非常有用的,可能將的仍是有限的,畢竟一些知識,還得在實踐的更深刻的掌握