【?,!!,?:,:: ,->,$,_(下劃線),@,as,..,{},,,】
1. 【?】
" ?" 修飾在成員變量的類型后面,表示這個變量可以為null,系統(tǒng)在任何情況下不會報它的空指針異常。
" ? " 修改在對象后面代表該對象如果為null則不處理后面邏輯
如果我們定義的變量是一個可以為空的類型,則要用【String?】
//在變量類型后面加上問號,代表該變量是可空變量
var name: String? = "eryang" //(PS:var和val關鍵字代表的意義會在另一篇幅中去著重講)
name= null // 編譯正確
var name: String = "eryang"
name= null // 編譯錯誤
val len = name.length // 錯誤:變量“b”可能為空
val len = name?.length //如果 name非空,就返回 name.length ,否則返回 null,這個表達式的類型是 Int? 。
let函數(shù)和?.同時使用來處理可空表達式
person?.let{
//內(nèi)部的it一定是非空的
}
//如果person為空就不會調(diào)用let函數(shù)
//如果person不為空才會調(diào)用let函數(shù),所以?.和let函數(shù)配合使用可以很方便的處理可空表達式
person?.let {
println(it.name) //輸出name
}
下面再舉個例子來深刻理解下let函數(shù):
// 沒有使用let函數(shù)的代碼是這樣的,看起來不夠優(yōu)雅
mVideoPlayer?.setVideoView(activity.course_video_view)
mVideoPlayer?.setControllerView(activity.course_video_controller_view)
mVideoPlayer?.setCurtainView(activity.course_video_curtain_view)
// 使用let函數(shù)后的代碼是這樣的
mVideoPlayer?.let {
it.setVideoView(activity.course_video_view)
it.setControllerView(activity.course_video_controller_view)
it.setCurtainView(activity.course_video_curtain_view)
}
2. 【!!】(Kotlin不推薦使用非空斷言,通常我們會用?:來防止程序運行時報空指針異常而崩潰)
" !! "放在對象后面代表該對象如果為null則拋出異常
" !! "放在方法傳遞實參后面,也是代表不能為null,為null會拋異常
val len = name!!.length//如果name不為空,則返回name.length,如果name為空,則拋出異常NullPointerException
使用斷!!可以很方便的在拋出空指針異常的時候定位到異常的變量的位置,但是千萬不要連續(xù)使用斷言!!
student!!.person!!.name//如果報空指針異常了則無法判斷到底是student為空還是person為空,所以不要連續(xù)使用斷言!!
" !!. "的用法就是相當于Java里的if()else()判斷null
val nullClass: NullClass?=null
nullClass!!.nullFun()
轉化成JAVA代碼
NullClass nullClass = null;
if (nullClass!=null) {
nullClass.nullFun();
}else {
throw new NullPointerException();
}
3.【?:】Elvis操作符
對象foo ?: 對象bar 表達式,意思為: 當對象 foo值為 null 時,那么它就會返回后面的對象 bar,否則返回foo。
val len = name?.length ?: -1//當name不為空時,返回name.length,當name為空時,返回-1
val roomList: ArrayList<Room>? = null
val mySize= roomList?.size ?: 0 //mySize 的值就為0,因為 roomList?.size 為空,
val roomList: ArrayList<FPXBean>? = null
if (roomList?.size ?: 0 > 0) { // 這一行添加了?:
Log.d("TAG", "-->> 列表數(shù)不是0")
}
4. 【::】
雙冒號操作符 表示把一個方法當做一個參數(shù),傳遞到另一個方法中進行使用,通俗的來講就是引用一個方法
反射
利用 ::,甚至可以享受到優(yōu)于 Java 的反射特性。
inline fun <reified T> T.foo3(string: String) {
Log.e(T::class.simpleName, string)
}
這里暫時不需要理會 inline 和 reified,我們看到,直接使用泛型 T:: 即可反射獲得其內(nèi)部屬性,如 class,constructor 等,這在 Java 中是不可能做到的。
//得到類的Class對象
startActivity(Intent(this@KotlinActivity, MainActivity::class.java))
::方法名
雙冒號跟方法名作用就是將方法作為參數(shù)傳入。
class ColonMagic {
/**
* 不需要參數(shù)
*/
private fun sayNoWords() {
println(" no msg ")
}
/**
* 一個參數(shù)
* message:String類型
*/
private fun say(message: String) {
println(message)
}
/**
* 兩個參數(shù)
* msg: String類型
* dosay: (String) -> Unit 一個參數(shù)為String不需要返回值的方法體
*/
private fun peopleDo(msg: String, doSay: (String) -> Unit) {
//doSay(msg)調(diào)用的就是傳入的say方法,即say(msg),只不過參數(shù)名是doSay本質(zhì)是say方法
//此處打印出來 I say ! 日志
doSay(msg)
}
fun people() {
//調(diào)用peopleDo方法,并傳入?yún)?shù) “I say !” 和 say方法體
//此處 ::say 是將say方法作為參數(shù)傳入peopleDo方法
//此處只是將say作為參數(shù),而不會調(diào)用say方法,也就不會println(message),不會輸出日志
peopleDo("I say !", ::say)
//此處報錯,因為需要的是(String) -> Unit; 一個String參數(shù),無返回值的方法體
//但是sayNoWords方法是沒有參數(shù)的,所以會報錯
peopleDo("I say !", ::sayNoWords)
}
}
fun main(msg: Array<String>) {
ColonMagic().people();
}
mian方法中調(diào)用 ColonMagic().people();打印日志如下:
I say !
方法作為參數(shù)傳入時不會被調(diào)用,所以最后只有一行 I say !日志。
fun main() {
foo2("xxx", this::bar2) //right
foo2("xxx", this::bar1) //wrong, compile fail
}
fun bar1() {
print("bar1")
}
fun foo2(content: String, body: (String) -> Unit) {
body(content)
}
fun bar2(string: String) {
print(string)
}
作為參數(shù)的函數(shù),該函數(shù)的參數(shù)類型和返回值類型一定要和規(guī)定的一致。上述的例子中bar1()是沒有任何參數(shù)類型的,而foo2中第二個參數(shù)body:(String) -> Unit ,需要一個字符串類型,所以 foo2("xxx", this::bar1) 這種寫法是錯誤的; foo2("xxx", this::bar1) 這種寫法是對的,因為bar2(string: String) 的參數(shù)類型是一個字符串,符合參數(shù)類型和返回值類型一定要和規(guī)定的一致。
- “->”符號操作符,單從形態(tài)上看,是一種流向和對應的關系。即前面的語句執(zhí)行之后,將執(zhí)行流轉到指向的語句,并且是對應的。
6.【$】符合和多行輸入符
val i = 10
println("i = $i") // prints "i = 10"
val s = "abc"
println("$s.length is ${s.length}") // prints "abc.length is 3"
如上面的代碼中,要把" i "連接到字符串中,模板表達式以美元符號($)開頭,由一個簡單名稱組成: $i
//三引號的形式用來輸入多行文本
val str = """
one
two
"""
//等價于
val str = "one\ntwo"
三引號之間輸入的內(nèi)容將被原樣保留,之中的單號和雙引號不轉義,其中的不可見字符比如/n和/t都會被保留。(\t是補全當前字符串長度到8的整數(shù)倍,最少1個最多8個空格,補多少要看你\t前字符串長度,比如當前字符串長度10,那么\t后長度是16,也就是補6個空格,如果當前字符串長度12,此時\t后長度是16,補4個空格)
7. 【_】(下劃線)
在Kotlin中,可以使用一個下劃線字符(_)作為lambda或表達式函數(shù)的參數(shù)的名稱,或作為解構條目的名稱。
①作為lambda函數(shù)的參數(shù)名稱
fun main(args: Array<String>) {
val aa = mapOf(1 to "a",2 to "B")
aa.forEach { key, value -> println("value:$value")
}
在上述示例中,只是用到了value值,key并沒有用到。這樣,我們就想不在聲明key,那么就需要使用下劃線字符(_)作為key替代,即:
fun main(args: Array<String>) {
val aa = mapOf(1 to "a",2 to "B")
aa.forEach { _, value -> println("value:$value")
}
②作為解構聲明的參數(shù)
解構聲明就是將一個對象解構(destructure)為多個變量,也就是意味著一個解構聲明會一次性創(chuàng)建多個變量。盡管這樣很方便,但是,如果用不到的變量必然也必須得聲明,從而造成了變量的冗余。Kotlin-解構聲明
data class Person(var age: Int, var name: String)
//Peron聲明了 age,name兩個變量。解構時如果只需要age這一個變量時
val person = Preson(18, "eryang")
val (age, _) = person
③數(shù)字字面值中的下劃線
Kotlin的數(shù)字面值可以使用下劃線來分隔數(shù)值進行分組:
val oneMillion = 1_000_000
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010
fun main(args: Array<String>) {
// Log: 1000000
println(oneMillion)
// Log: ffecde5e
println(hexBytes.toString(16))
// Log: 11010010011010011001010010010010
println(bytes.toString(2))
}
8.【@】
①限定this的類型
class User {
inner class State{
fun getUser(): User{
//返回User
return this@User
}
fun getState(): State{
//返回State
return this@State
}
}
}
②作為標簽
跳出雙層for
loop@ for (itemA in arraysA) {
var i : Int = 0
for (itemB in arraysB) {
i++
if (itemB > 2) {
break@loop
}
println("itemB:$itemB")
}
}
命名函數(shù)自動定義標簽
fun fun_run(){
run {
println("lambda")
}
var i: Int = run {
return@run 1
}
println("$i")
//匿名函數(shù)可以通過自定義標簽進行跳轉和返回
i = run (outer@{
return@outer 2
})
println(i)
}
從forEach函數(shù)跳出
fun forEach_label(ints: List<Int>)
{
var i =2
ints.forEach {
//forEach中無法使用continue和break;
// if (it == 0) continue //編譯錯誤
// if (it == 2) /*break //編譯錯誤 */
print(it)
}
run outer@{
ints.forEach {
if (it == 0) return@forEach //相當于在forEach函數(shù)中continue,實際上是從匿名函數(shù)返回
if (it == 2) return@outer //相當于在forEach函數(shù)中使用break,實際上是跳轉到outer之外
}
}
if (i == 3)
{
//每個函數(shù)的名字代表一個函數(shù)地址,所以函數(shù)自動成為標簽
return@forEach_label //等同于return
}
}
9.【as】類型轉換運算符
①“不安全的” 類型轉換操作符
如果類型轉換不成功,類型轉換操作符通常會拋出一個異常。因此,我們稱之為 不安全的(unsafe)。在 Kotlin 中,不安全的類型轉換使用中綴操作符 as
val y = null
val x: String = y as String
// 輸出
注意 null 不能被轉換為 String,因為這個類型不是 可為 null 的(nullable),也就是說,如果 y 為 null,上例中的代碼將拋出一個異常。為了實現(xiàn)與 Java 相同的類型轉換,我們需要在類型轉換操作符的右側使用可為 null 的類型,比如:
val y = null
val x: String? = y as String?
println("x = $x") // x = null
上述代碼,表示允許 String 可空,這樣當 y = null 時,不會拋異常;但是,當類型轉換失敗時,還是會崩潰,如下:
val y = 66
val x: String? = y as String?
②“安全的” (nullable) 類型轉換操作(as?)
為了避免拋出異常,你可以使用 安全的 類型轉換操作符 as?,當類型轉換失敗時,它會返回 null,但不會拋出異常崩潰:
val y = 66
val x: String? = y as? String
println("x = $x") // x = null
val y = null
val x: String? = y as? String
println("x = $x") // x = null
嘗試把值轉換成給定的類型,如果類型不合適就返回null
10.【..】
Kotlin中有區(qū)間的概念,區(qū)間表達式由具有操作符形式 .. 的 rangeTo 函數(shù)輔以 in 和 !in 形成。 區(qū)間是為任何可比較類型定義的,但對于整型原生類型,它有一個優(yōu)化的實現(xiàn)。以下是使用區(qū)間的一些示例:
if (i in 1..10) { // 等同于 1 <= i && i <= 10 (1 <= i <= 10)
println(i)
}
//使用until函數(shù),創(chuàng)建一個不包括其結束元素的區(qū)間
for (i in 1 until 10) { // i in [1, 10) 排除了 10
println(i)
}
for (i in 1..4) print(i) // 輸出“1234”
for (i in 4..1) print(i) // 什么都不輸出
如果你想倒序迭代數(shù)字呢?也很簡單。你可以使用標準庫中定義的 downTo() 函數(shù)
for (i in 4 downTo 1) print(i) // 輸出“4321”
step()函數(shù),可以指定任意步長
for (i in 1..4 step 2) print(i) // 輸出“13”
for (i in 4 downTo 1 step 2) print(i) // 輸出“42”
11.【{}】
這里指的是lambda表達式的符號。
// 一個參數(shù)
var callback: ((str: String) -> Unit)? = null
callback = { println(it)}
// 判斷并使用
callback?.invoke("hello")
//兩個參數(shù)
var callback2: ((name: String, age: Int) -> Unit)? = null
callback2 = { hello: String, world: Int -> println("$hello's age is $world") }
callback2?.invoke("Tom", 22)
var callback3 :((num1:Int, num2: Int)->String)? = null
//類型可以推斷
callback3 = { num1, num2 ->
var res:Int = num1 + num2
res.toString()
}
println(callback3?.invoke(1, 2))
kotlin中{}里面整個是lambda的一個表達式,而java8中{}部分只是lambda表達式的body部分。
12.【..】
13.【..】
14.【..】
15.【..】
參考:
1:https://blog.csdn.net/lckj686/article/details/80448471
2:https://blog.csdn.net/u011288271/article/details/106495785
3:https://blog.csdn.net/u011489043/article/details/95175506
4:https://blog.csdn.net/zxc123e/article/details/73368781