幾個特性,快速上手Kotlin

2020-12-18 碼農登陸

前言

因為工作需要,所以最近大量的時間都用在了對Kotlin的提升上。所以最近的文章基本都是關於Kotlin的了內容。

這不是一個死扣細節的文章,而是一個幫助小夥伴們快速由Java(Android)轉向Kotlin的文章,因此更多的是一種語法與思想混在的模式。

正文

不多扯淡,點進來的小夥伴們肯定都是來學技術的,開搞。

一、屬性

各位,千萬不要因為標題的屬性,就覺得沒什麼營養,以為我要說什麼var、val。不不不,往下看,kotlin中的屬性大有文章。

1.1、可觀察屬性Observable

這個語言特性,是非常非常用意思,且實用的。不信?往下看,來一個demo:

classUser{var name: String by Delegates.observable("") {prop, old, new -> println("$old -> $new") }}funmain() {val user = User() user.name = "first" user.name = "second"}

效果好不好,咱們看「療效」,運行結果如下:

是不是覺得有點意思?我們name這個屬性的任何變化,都被觀察了。我們可以在回調函數中,「胡搞瞎搞」~至於怎麼搞?whatever。有小夥伴可能想說,既然我監聽了屬性的變化,我可以不以偷偷改些屬性呢?當然可以,不過我們需要下面這個函數。

1.2、vetoable

vetoable比較有意思,可以理解為一個hook函數,它接受一個用於判斷的表達式,滿足這個表達式的才會被賦值,否則丟棄。很簡單,上demo:

// 過濾不滿足條件的setvar max: Intby Delegates.vetoable(0) { property, oldValue, newValue ->newValue > oldValue}println(max) // 0max = 10println(max) // 10max = 5println(max) // 10

2.1、延遲屬性 Lazy

官方解釋(羅裡吧嗦,不看也罷)lazy() 是接受一個 lambda 並返回一個 Lazy

說白了就是懶加載。被lazy標識的變量,在被調用的時候會觸發我們實現的表達式,

(下文會重點聊表達式,這裡我們只需要之後,表達式的最後一行,代表著return)

並拿到表達式的返回值。但表達式只會執行一次,後續的調用,直接回去表達式的返回值。也就是我們常說的懶加載。

val lazyValue: String by lazy {println("computed!")"Hello"}funmain() { println(lazyValue) println(lazyValue)}

看結果我們就很清晰了吧。

委託

上邊說的這些東西,在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中表示: 如果左側的值為空,就取右側的值。

更多有趣的符號用法,可以參考官網:https://www.kotlincn.net/docs/reference/keyword-reference.html

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 表達式中,但退出包含它的函數)稱為非局部返回。)

inlinefuninlined(block: () -> Unit) {println("hi!")}funfoo() { inlined {return// OK:該 lambda 表達式是內聯的 }}funmain() { foo()}

比如,這種return是合法的,因為foreach是內聯函數

fun hasZeros(ints: List): Boolean {ints.forEach { if (it == 0) return true // 從 hasZeros 返回 } return false}public inline fun Iterable.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) {in1..10 -> print("x is in the range")in validNumbers -> print("x is valid")!in10..20 -> print("x is outside the range")else -> print("none of the above")}

另一種用法是檢測一個值是(is)或者不是(!is)一個特定類型的值。

由於智能轉換,我們可以訪問該類型的方法與屬性而無需任何額外的檢測。

funhasPrefix(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 來源的標籤:

classA{ // 隱式標籤 @Ainner classB{ // 隱式標籤 @BfunInt.foo() { // 隱式標籤 @fooval a = this@A// A 的 thisval b = this@B// B 的 thisval c = this// foo() 的接收者,一個 Intval c1 = this@foo// foo() 的接收者,一個 Intval 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這個函數來解釋這個問題:

funlock(lock: Lock, body: () -> T): T {lock.lock()try {return body() }finally { lock.unlock() }}

正常我們會這樣調用這個函數

lock(l) { foo() }

。如果不加修飾的話,因為高階函數的存在,會造成大量的對象對創建出來,所以我們這裡我們需要用內聯的方式消除這種額外。

// 使用內聯,代碼編譯後將變成這樣l.lock()try {foo()}finally { l.unlock()}

可能有小夥伴在這裡會有一些懵逼,一時沒有理解內聯做了什麼。接下來用三個函數,解釋一下這個問題:

// 一個函數中,調用了另一個函數。虛擬機勢必要為funtion1的調用,增加很多開銷。funfuntion(){var a =1+1+1funtion1()}funfuntion1(){var aa =1+1+1var bb =1+1+1var cc =1+1+1}// 那麼我們使用內聯之後,就變成了這樣:funfuntion(){var a =1+1+1var aa =1+1+1var bb =1+1+1var cc =1+1+1}

我們可以看到,內聯之後,我們funtion1函數中的實現,仿佛就在funtion函數中一樣。因此就相應的降低了這部分的消耗。

內聯可能導致生成的代碼增加;不過如果我們使用得當(即避免內聯過大函數),性能上會有所提升,尤其是在循環中的「超多態(megamorphic)」調用處。

尾聲

OK,到此差不多關於Kotlin的基於語法的內容差不多就要告一段落了。接下來的文章基本就會圍繞著Android中的Kotlin進行開展。希望各位小夥伴們可以從中有所收穫呦~

相關焦點

  • Kotlin 怎麼學 ?遇到過哪些坑?
    然後就可以像 java 一樣使用 kotlin,當然這不是我們的最終目的,因為 kotlin 的語法糖才是我們最後的目標。google 發布了 kotlin 簡易教程,大概只需要幾個小時,就可以看完:在實際的開發中,大概第一個星期內,你寫代碼的速度會下降一些,但是一個星期之後,完全上手了,寫代碼的速度是有很大的提升的。
  • Kotlin 一統天下?Kotlin/Native 支持 iOS 和 Web 開發
    此外,值得關注的就是協程這個特性了。雖然協程仍然被標記為實驗性狀態,但官方特意說明了這裡「實驗性」代表的含義。官方表示協程已經完全準備好用於生產環境,他們也已使用協程進行開發,而且也沒發現在使用當中出現任何重大問題。之所以仍保持實驗性狀態,是為了能夠對 Kotlin 繼續進行設計迭代。根據目前的計劃,Kotlin 1.3 將會刪除協程的實驗性狀態。
  • 最強總結 | 帶你快速搞定kotlin開發(上篇)
    fun sum(a: Int, b: Int): Int {     return a + b}  // 對於只有一行的函數,kotlin可以這麼簡寫,爽不?val可以理解為Java中的屬性聲明加上了final關鍵字(將kotlin的字節碼反編譯成Java一看就知道了),其實kotlin是更傾向於推薦使用val來聲明變量,這是一種防禦性的編碼思維模式,目的是減少程序出錯或者變的更加安全。
  • Kotlin 測試利器—MockK
    店鋪二維碼:來源:http://www.51testing.com 為什麼需要MockK  在MockK之前,已經有一大批測試庫可以用於Mocking,其中有名的也有很多,比如Mockito, PowerMock,Jmockit等等,但是他們都有各自的缺陷,這些缺陷也和Kotlin的特性有關
  • Kotlin入門教程,快使用Kotlin吧
    學習網站 Kotlin 從入門到放棄:https://www.jianshu.com/c/d3eac4c37b5fKotlin 菜鳥教程:http://www.runoob.com/kotlin/kotlin-tutorial.html基本語法 1、數據類型1.1、基本數據類型類型位寬度Double64Float32Long64Int32Short16Byte8每一個類型都有一個toXXX
  • 使用 Kotlin 進行 Android 測試
    Kotlin 程式語言開始之前,我會給這個後起之秀的主要好處來個快速總結:Kotlin 很簡潔。代碼寫得少,錯誤犯的少。Kotlin 非常具有表現力。你想用簡短的方式表達任何東西。Kotlin 非常實用。不需要繞來繞去直擊心底。Kotlin 對 Android 非常友好。一會兒你就能看到了。
  • Google 加持的 Kotlin 真能取代它?
    又是什麼阻礙了開發者上手 Kotlin?對此,JetBrains 列出了幾個主要觀點:開發者對 Kotlin 技術知識的缺乏;市場對於該語言的採用率低;開發者並不想學習新的語言;Kotlin 並沒有價值;生態系統薄弱;.
  • 問:談談 Kotlin 範型與逆變協變?
    kotlin 系列文章均以 Java 差異為核心進行提煉,與 Java 相同部分不再列出。隨著 kotlin 官方版本的迭代,文中有些語法可能會發生變化,請務必留意。kotlin 泛型(generics)與 java 一樣,kotlin 的泛型用來表示變量類型的參數化。
  • 閒聊 Kotlin-Native (0) - 我們為什麼應該關注一下 Kotlin Native?
    因為現在的程式語言實在太多了,新語言出來必然要解決現有某個語言的痛點,這樣才能快速切入該語言所覆蓋的領域。Kotlin 也是這樣在當年嶄露頭角的,要不是 Android 上沒有很好的替代語言,估計 Kotlin 也不會這麼快進入大家的視野。
  • Kotlin學習筆記——基礎篇
    1.基礎語法英文官網:https://kotlinlang.org/docs/reference/中文官網:https://www.kotlincn.net/docs/reference/協程學習資料:https://www.kotlincn.net/docs/reference/coroutines-overview.html
  • Kotlin函數式編程
    getNumResult{ a, b -> a + b }==> value = 3var value = getNumResult{ a, b -> a * b }==> value = 2依照函數類型所示,a和b對應兩個Int類型的參數,這個名稱可以任意定義,a+b為返回值,隨後return 了一個lambda的返回值,看起來是不是很強大,而且依照Kotlin的特性
  • Java到Kotlin,從入門到放棄
    去年冬天,在經過幾個月的日息相處之後,我們做了技術總結,最後的結論是Kotlin並沒有給我們代來什麼改進,反而拖慢了我們的進度。所以我們從入門到放棄,又回歸了Java 10。命名隱藏(Name shadowing)隱藏(shadowing)是Kotlin帶給我們的最大驚喜。
  • Kotlin VS Java 編譯速度大比拼,到底誰更快?
    但是你通常只會對幾個文件進行更改後編譯,增量構建將有不同的性能。 所以,讓我們來看看Kotlin在增量編譯是否可以趕上。增量構建編譯器最重要的性能特性之一是使用增量編譯。 正常構建將重新編譯項目中的所有源文件,但是增量構建將跟蹤自上次構建以來哪些文件已更改,並且只重新編譯這些文件和依賴它們的文件。 這可能對編譯時間有巨大的影響,特別是對於大型項目。
  • Kotlin 都轉正成 Android 官方語言了,你還不試一下?
    為了讓大家更快了解和上手 Kotlin,掘金技術社區為大家整理了這份 Kotlin 資源大全,希望可以幫助大家用最短時間學習 Kotlin.如何參與本項目?如果有興趣,請加微信:404096378,備註「kotlin大全」官網及文檔中文社區教程 & 文章視頻教程開始用 Kotlin 之前Kotlin 語言程序設計上手 KotlinKotlin & Java開發實踐更多開源庫和框架Android
  • Kotlin項目實戰之手機影音---悅單條目實現及BaseListFragment抽取
    由於比較簡單,這裡就快速來實現一下,順便過一遍上一次實現的首頁Tab當時構建的邏輯。設置布局:由於它的布局跟首頁Tab是一模一樣的,所以可以直接復用:com.kotlin.musicplayer.net.ResponseHandlerimport com.kotlin.musicplayer.net.YueDanRequestimport com.kotlin.musicplayer.presenter.
  • Kotlin協程優雅的與Retrofit纏綿
    作者:limuyang2,連結:https://juejin.im/post/5cfb38f96fb9a07eeb139a00Kotlin已經成為Android開發的Google第一推薦語言,項目中也已經使用了很長時間的kotlin了,加上Kotlin1.3的發布,kotlin協程也已經穩定了
  • 小炒菜快速上手的幾個要點!
    我是菜市街小鋒,一個看到一個菜就想去學著做的小UP主,前面兩期的【一分鐘學做菜系列】給大家介紹了10種小炒菜的快速做法,第一次見面的各位可以打開我主頁看一下,都是經過本人親自製作後總結的經驗。本想著繼續做炒菜篇的,由於這幾天有事耽擱了,所以這期就介紹下家庭炒菜的幾個要點吧,這樣大家要學的話,上手也快。
  • 一款純Kotlin編寫的開源安卓應用 "Smile"
    首先,選擇一個完整的 kotlin 文檔,從第一章開始閱讀,尤其是最基礎的基本類型、控制流等等,千萬不能跳過,邊讀邊把知識點記錄下來,期間可以閱讀一些博客了解一下別人對這些知識點的理解,但是學習文檔是主線,一定不能偏離。這個過程我持續了三天,主要學習的文檔為: Kotlin 語言中文站 ,期間每學到一個知識點我就記錄下來,展示一部分:
  • 一些 Kotlin 小技巧及解析
    結合著 Kotlin 的高級函數的特性可以讓代碼可讀性更強,更加簡潔,但是呢簡潔的背後是有代價的,使用不當對性能可能會有損耗,這塊往往很容易被我們忽略,這就需要我們去研究 kotlin 語法糖背後的魔法,當我們在開發的時候,選擇合適的語法糖,儘量避免這些錯誤,關於 Kotlin 性能損失那些事,可以看一下我另外兩篇文章。
  • Kotlin 1.2.70 發布,增量編譯速度提高 7 倍
    需注意的是,增量編譯支持目前仍處於實驗階段,默認禁用,體驗前需手動啟用:在 Gradle 項目中,添加  kotlin.incremental.js=true到 gradle.properties或 local.properties在使用 IntelliJ IDEA 構建的項目中,轉到 Settings | Build, Execution, Deployment