幾個特性,快速上手Kotlin
前言
因為工作需要,所以最近大量的時間都用在了對Kotlin的提升上。所以最近的文章基本都是關於Kotlin的了內容。
這不是一個死扣細節的文章,而是一個幫助小夥伴們快速由Java(Android)轉向Kotlin的文章,因此更多的是一種語法與思想混在的模式。
正文
不多扯淡,點進來的小夥伴們肯定都是來學技術的,開搞。
一、屬性
各位,千萬不要因為標題的屬性,就覺得沒什麼營養,以為我要說什麼var、val。不不不,往下看,kotlin中的屬性大有文章。
1.1、可觀察屬性Observable
這個語言特性,是非常非常用意思,且實用的。不信?往下看,來一個demo:
class User { var name: String by Delegates.observable("<no name>") { prop, old, new -> println("$old -> $new") } }fun main() { val user = User() user.name = "first" user.name = "second"}
效果好不好,我們們看“療效”,執行結果如下:
image
是不是覺得有點意思?我們name這個屬性的任何變化,都被觀察了。我們可以在回撥函式中,“胡搞瞎搞”~至於怎麼搞?whatever。
有小夥伴可能想說,既然我監聽了屬性的變化,我可以不以偷偷改些屬性呢?當然可以,不過我們需要下面這個函式。
1.2、vetoable
vetoable比較有意思,可以理解為一個hook函式,它接受一個用於判斷的表示式,滿足這個表示式的才會被賦值,否則丟棄。很簡單,上demo:
// 過濾不滿足條件的setvar max: Int by Delegates.vetoable(0) { property, oldValue, newValue -> newValue > oldValue } println(max) // 0max = 10println(max) // 10max = 5println(max) // 10
2.1、延遲屬性 Lazy
官方解釋(羅裡吧嗦,不看也罷)
lazy() 是接受一個 lambda 並返回一個 Lazy <T> 例項的函式,返回的例項可以作為實現延遲屬性的委託: 第一次呼叫 get() 會執行已傳遞給 lazy() 的 lambda 表示式並記錄結果, 後續呼叫 get() 只是返回記錄的結果。
說白了就是懶載入。被lazy標識的變數,在被呼叫的時候會觸發我們實現的表示式,
(下文會重點聊表示式,這裡我們只需要之後,表示式的最後一行,代表著return)
並拿到表示式的返回值。但表示式只會執行一次,後續的呼叫,直接回去表示式的返回值。也就是我們常說的懶載入。
val lazyValue: String by lazy { println("computed!") "Hello"}fun main() { println(lazyValue) println(lazyValue) }
image
看結果我們就很清晰了吧。
委託
上邊說的這些東西,在Kotlin之中都統一稱之為委託/委託屬性。委託是比較好用的一種語言特性,甚至可以很巧妙的幫我們解決一些複雜的設計模式上的問題。
這其中的有趣,還望小夥伴們自己去探索呦~
二、表示式
對於我的學習來說,表示式的不理解,最開始對我閱讀程式碼造成了很大的困惑。主要是少了“相濡以沫”的return,搞得自己有點懵。所以這裡,讓我們聊一聊表示式,也算填了上文挖的坑。
1.1、if表示式
if這個關鍵字,在Kotlin中代表一個表示式,它會預設有一個return,就是表示式中的最後一行。比如:
var num = if(...某個判斷){ 666}else{ 66666}
這裡num的值就是666。既然我們的if有自帶return的功能,那麼我們Java中常用的?:(三元運算子)是不是就沒辦法用了?的確要三元運算子(條件 ? 然後 : 否則)在Kotlin中換了寫法(但並非不能用),因為普通的 if 就能勝任這個角色。
// 作為表示式val max = if (a > b) a else bval max = if (a > b) { print("Choose a") a } else { print("Choose b") b }
?:在Kotlin中表示: 如果左側的值為空,就取右側的值。
更多有趣的符號用法,可以參考官網:
1.2、關於return
既然我們從if中,瞭解了if中的隱式return,那這裡可能會有一個疑問,能不能顯示的寫一個return呢?答案是:不行。
因為在Kotlin中,return的語義是這樣的:從最直接包圍它的函式或者匿名函式返回。
表示式不屬於函式,所以不行,同樣Lambda表示式也不行。不這裡有些特殊情況,所以我們好好聊一聊:
要退出一個 Lambda 表示式,我們必須使用一個標籤,並且在 Lambda 表示式內部禁止使用裸 return,因為 Lambda 表示式不能使包含它的函式返回:
// 這裡forEach是一個Lambda表示式,我們使用**標籤**的形式,使其returnlistOf(1, 2, 3, 4, 5).forEach lit@{ if (it == 3) return@lit // 區域性返回到該 lambda 表示式的呼叫者,即 forEach 迴圈 print(it) }
這裡肯定有小夥伴質疑:我可以在forEach裡直接return啊!沒錯,的確是可以。因為forEach行內函數。行內函數是可以return的:
關於行內函數後文會有篇幅展開它。
官方介紹:內聯是被允許的(這種返回(位於 lambda 表示式中,但退出包含它的函式)稱為非區域性返回。)
inline fun inlined(block: () -> Unit) { println("hi!") }fun foo() { inlined { return // OK:該 lambda 表示式是內聯的 } }fun main() { foo() }
比如,這種return是合法的,因為foreach是行內函數
fun hasZeros(ints: List<Int>): Boolean { ints.forEach { if (it == 0) return true // 從 hasZeros 返回 } return false}public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit { for (element in this) action(element) }
嘚吧嘚說了這麼,日常能用到麼?說實話,沒有diao用。不過,遇到了我們們知道該如何解釋,這也算是一種收穫吧。
2.1、When 表示式
when是我們常用的switch的增強plus版。其最簡單的形式如下:
when (x) { 1 -> print("x == 1") 2 -> print("x == 2") else -> { // 注意這個塊 print("x is neither 1 nor 2") } }
我們也可以檢測一個值在(in)或者不在(!in)一個區間或者集合中。即滿足某些條件:
when (x) { in 1..10 -> print("x is in the range") in validNumbers -> print("x is valid") !in 10..20 -> print("x is outside the range") else -> print("none of the above") }
另一種用法是檢測一個值是(is)或者不是(!is)一個特定型別的值。
由於智慧轉換,我們可以訪問該型別的方法與屬性而無需任何額外的檢測。
fun hasPrefix(x: Any) = when(x) { is String -> x.startsWith("prefix") else -> false}
因為,說when是switch的plus不為過吧。更多有趣的用法,歡迎各位小夥伴留言補充呦~
三、雜亂的小細節
1.1、Class
val c = MyClass::class
返回的是KClass,如果我們需要Class,則要這樣MyClass::class.java
1.2、this
要訪問來自外部作用域的this(一個類 或者擴充套件函式, 或者帶標籤的帶有接收者的函式字面值)我們使用this@label,其中 @label 是一個代指 this 來源的標籤:
class A { // 隱式標籤 @A inner class B { // 隱式標籤 @B fun Int.foo() { // 隱式標籤 @foo val a = this@A // A 的 this val b = this@B // B 的 this val c = this // foo() 的接收者,一個 Int val c1 = this@foo // foo() 的接收者,一個 Int val funLit = lambda@ fun String.() { val d = this // funLit 的接收者 } val funLit2 = { s: String -> // foo() 的接收者,因為它包含的 lambda 表示式 // 沒有任何接收者 val d1 = this } } } }
1.3、匿名函式
var list = arrayListOf(1, 2, 3, 4, 5, 6) // lambda表示式 list.filter { it > 3 } // 規規矩矩的匿名函式 list.filter(fun(it): Boolean { return it > 3 }) // 自動型別推斷後簡寫的匿名函式 list.filter(fun(it) = it > 3)
對於具有表示式函式體的匿名函式將自動推斷返回型別,而具有程式碼塊函式體的返回型別必須顯式指定(或者已假定為 Unit)
匿名函式引數總是在括號內傳遞。 允許將函式留在圓括號外的簡寫語法僅適用於 lambda 表示式。
Lambda表示式與匿名函式之間的另一個區別是非區域性返回的行為。一個不帶標籤的 return 語句總是在用 fun 關鍵字宣告的函式中返回。這意味著 lambda 表示式中的 return 將從包含它的函式返回,而匿名函式中的 return 將從匿名函式自身返回。
1.4、行內函數
使用高階函式(關於高階函式,可以看我之前的一篇文章)會帶來一些執行時的效率損失。因為每一個函式都是一個物件,並且會捕獲一個閉包( 即那些在函式體內會訪問到的變數)。 我們都清楚,記憶體分配(對於函式物件和類)和虛擬呼叫會引入執行時的開銷。所以此時就需要行內函數,來消除這種開銷。
官方使用了lock這個函式來解釋這個問題:
fun <T> lock(lock: Lock, body: () -> T): T { lock.lock() try { return body() } finally { lock.unlock() } }
正常我們會這樣呼叫這個函式lock(l) { foo() }
。如果不加修飾的話,因為高階函式的存在,會造成大量的物件對建立出來,所以我們這裡我們需要用內聯的方式消除這種額外。
// 使用內聯,程式碼編譯後將變成這樣l.lock()try { foo() }finally { l.unlock() }
可能有小夥伴在這裡會有一些懵逼,一時沒有理解內聯做了什麼。接下來用三個函式,解釋一下這個問題:
// 一個函式中,呼叫了另一個函式。虛擬機器勢必要為funtion1的呼叫,增加很多開銷。fun funtion(){ var a =1+1+1 funtion1() }fun funtion1(){ var aa =1+1+1 var bb =1+1+1 var cc =1+1+1}// 那麼我們使用內聯之後,就變成了這樣:fun funtion(){ var a =1+1+1 var aa =1+1+1 var bb =1+1+1 var cc =1+1+1}
我們可以看到,內聯之後,我們funtion1函式中的實現,彷彿就在funtion函式中一樣。因此就相應的降低了這部分的消耗。
內聯可能導致生成的程式碼增加;不過如果我們使用得當(即避免內聯過大函式),效能上會有所提升,尤其是在迴圈中的“超多型(megamorphic)”呼叫處。
作者:MDove
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2618/viewspace-2821613/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 幾個小實踐帶你快速上手MindSpore
- 充分理解Kotlin,快速上手寫業務Kotlin
- 快速上手Kotlin開發-張濤-極客時間Kotlin
- Blog-05-《一週快速上手Kotlin For Android》-KotlinAndroid
- 快速上手第一個智慧合約
- Redis 快速上手Redis
- 快速上手BootstrapVuebootVue
- ElasticJob 快速上手AST
- Flask快速上手Flask
- JDBC快速上手JDBC
- docker 快速上手Docker
- Flask之旅: 快速上手Flask
- Vue框架快速上手Vue框架
- 快速上手之dockerDocker
- PHP 7 的幾個新特性PHP
- Kotlin的幾個擴充套件函式Kotlin套件函式
- 如何找到並快速上手一個開源專案
- 快速上手前端框架layui前端框架UI
- Git和GitHub快速上手Github
- Docker 快速上手指南Docker
- Zalando Postgres Operator 快速上手
- 快速上手 vue3Vue
- LLaMA快速上手指南
- Git 快速上手精華梳理Git
- vue.js快速上手Vue.js
- react native快速上手React Native
- Vue.js 快速上手。Vue.js
- 資訊科技快速上手教程
- gdb除錯快速上手除錯
- Vue3快速上手Vue
- 一個系列搞定MyBatis:MyBatis快速上手增刪改查MyBatis
- PWA介紹及快速上手搭建一個PWA應用
- 【零基礎上手JavaWeb】快速上手 JavaScript DOM操作WebJavaScript
- 微軟C# 8.0中的幾個特性微軟C#
- Flutter ListView 實戰快速上手FlutterView
- SAP系統如何快速上手?
- React 教程:快速上手指南React
- Spring-Boot快速上手Springboot