最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

From Java To Kotlin:空安全、擴(kuò)展、函數(shù)、Lambda很詳細(xì),這次終于懂了

2023-05-20 11:06 作者:Seachal  | 我要投稿

From Java To Kotlin, 空安全、擴(kuò)展、函數(shù)、Lambda

概述(Summarize)

  • Kotlin 是什么?

  • 可以做什么?

  • Android 官方開發(fā)語言從Java變?yōu)镵otlin,Java 有哪些問題?

  • Kotlin的優(yōu)點(diǎn)

  • Kotlin 特性(Features)

Kotlin 是什么?

Kotlin 出自于捷克一家軟件研發(fā)公司 JetBrains ,這家公司開發(fā)出很多優(yōu)秀的 IDE,如 IntelliJ IDEA、DataGrip 等都是它的杰作,包括 Google 官方的 Android IDE -- Android Studio ,也是 IntelliJ IDEA 的插件版。

Kotlin 源于 JetBrains 的圣彼得堡團(tuán)隊(duì),名稱取自圣彼得堡附近的一個(gè)小島 ( Kotlin Island ) ,和 Java一樣用島嶼命名,JetBrains 在 2010 年首次推出 Kotlin 編程語言,并在次年將之開源。

  • Kotlin 是一種在 Java 虛擬機(jī)上運(yùn)行的靜態(tài)類型編程語言,被稱之為 Android 世界的Swift。

  • Kotlin 可以編譯成Java字節(jié)碼。也可以編譯成 JavaScript,方便在沒有 JVM 的設(shè)備上運(yùn)行。

  • 在Google I/O 2017中,Google 宣布 Kotlin 成為 Android 官方開發(fā)語言,替代 Java 語言。

Kotlin 代碼會(huì)被編譯成Java字節(jié)碼,所以和 Java 兼容

可以做什么?

  • Android

  • Server-side

  • Multiplatform Mobile

    Kotlin Multiplatform Mobile is in Beta!

  • Multiplatform libraries

    Create a multiPlatform library for JVM, JS, and Native platforms.

  • 可以做很多方向的開發(fā)!

Android 官方開發(fā)語言從Java變?yōu)镵otlin,Java 有哪些問題?

  • 空引用(Null references):Java 中的 null 值是經(jīng)常導(dǎo)致程序運(yùn)行出錯(cuò)的原因之一,因?yàn)?Java 不支持空安全。

  • 更少的函數(shù)式編程特性:Java 語言在函數(shù)式編程方面的支持相對(duì)較弱,雖然 Java 8 引入了 Lambda 表達(dá)式和 Stream API,但是 Kotlin 語言在這方面的支持更加全面和友好。

  • 不夠靈活,缺乏擴(kuò)展能力:我們不能給 第三方 SDK 中的classes 或者 interfaces 增加新的方法。。

  • 語法繁瑣,不夠簡(jiǎn)潔:Java 語言比 Kotlin 語言更為冗長(zhǎng),需要寫更多的代碼來完成相同的任務(wù),這可能會(huì)降低開發(fā)效率。


Kotlin的優(yōu)點(diǎn)

Modern, concise and safe programming language

  • 簡(jiǎn)約:使用一行代碼創(chuàng)建一個(gè)包含 getters、 setters、 equals()、 hashCode()、 toString() 以及 copy() 的 POJO:

  • 安全:徹底告別那些煩人的 NullPointerException

  • 互操作性: Kotlin 可以與 Java 混合編程,Kotlin 和 Java 可以相互調(diào)用,目標(biāo)是 100% 兼容。

Kotlin 特性(Features)

  • 空安全(Null safety)

  • 類型推斷(Type inference)

  • 數(shù)據(jù)類 (Data classes)

  • 擴(kuò)展函數(shù) (Extension functions)

  • 智能轉(zhuǎn)換(Smart casts)

  • 字符串模板(String templates)

  • 單例(Singletons)

  • 函數(shù)類型 (Function Type )

  • Lambda 表達(dá)式

  • 高階函數(shù)(Primary constructors)

  • 函數(shù)字面量和內(nèi)聯(lián)函數(shù)(Function literals & inline functions)

  • 類委托(Class delegation)

  • 等等......

基本語法 (Basic Syntax )

  • 變量(Variables)

  • 基本數(shù)據(jù)類型( Basic Data Type )

  • 空安全(Null Safety )

  • 函數(shù)聲明( Define Function )

  • 讓函數(shù)更好的調(diào)用( Making functions easier to call )

    • 命名參數(shù)/具名參數(shù) (Named arguments)

    • 參數(shù)默認(rèn)值(Default arguments)

變量(Variables)

在 Java/C 當(dāng)中,如果我們要聲明變量,我們必須要聲明它的類型,后面跟著變量的名稱和對(duì)應(yīng)的值,然后以分號(hào)結(jié)尾。就像這樣:

Integer price = 100;

而 Kotlin 則不一樣,我們要使用val或者是var這樣的關(guān)鍵字作為開頭,后面跟“變量名稱”,接著是“變量類型”和“賦值語句”,最后是分號(hào)結(jié)尾。就像這樣:

/*關(guān)鍵字 ? ? 變量類型 ↓ ? ? ? ? ?↓ ? ? ? ? ? */var price: Int = 100; ? /* ? ? ↑ ? ? ? ? ? ?↑ ? 變量名 ? ? ? ?變量值 ? */

在 Kotlin 里面,代碼末尾的分號(hào)省略不寫,就像這樣:

var price = 100 // 默認(rèn)推導(dǎo)類型為: Int


另外,由于 Kotlin 支持類型推導(dǎo),大部分情況下,我們的變量類型可以省略不寫,就像這樣:

var price = 100 // 默認(rèn)推導(dǎo)類型為: Int

var 聲明的變量,我們叫做可變變量,它對(duì)應(yīng) Java 里的普通變量。

val 聲明的變量,我們叫做只讀變量,它相當(dāng)于 Java 里面的 final 變量。

var price = 100price = 101val num = 1num = 2 // 編譯器報(bào)錯(cuò)



var, val 反編譯成 Java :

我們已經(jīng)知道了 val 屬性只有 getter,只能保證引用不變,不能保證內(nèi)容不變。例如,下面的代碼:

class PersonZ { ? ?var name = "zhang" ? ?var age = 30 ? ?val nickname: String ? ? ? ?get() { ? ? ? ? ? ?return if (age > 30) "laozhang" else "xiaozhang" ? ? ? ?} ? ?fun grow() { ? ? ? ?age += 1 ? ?}

屬性 nickname 的值并非不可變,當(dāng)調(diào)用 grow() 方法時(shí),它的值會(huì)從 "xiaozhang" 變?yōu)?"laozhang",

不過因?yàn)闆]有 setter,所以無法直接給 nickname 賦值

編譯時(shí)常量

const 只能修飾沒有自定義 getter 的 val 屬性,而且它的值必須在編譯時(shí)確定。

val time = System.currentTimeMillis()// 這種會(huì)報(bào)錯(cuò)const val constTime = System.currentTimeMillis()

基本數(shù)據(jù)類型( Basic Data Type )

Kotlin 的基本數(shù)值類型包括 Byte、Short、Int、Long、Float、Double 等。

類型位寬度備注Double64Kotlin 沒有 doubleFloat32Kotlin 沒有 floatLong64Kotlin 沒有 longInt32Kotlin 沒有 int/IntegeShort16Kotlin 沒有 shortByte8Kotlin 沒有 byte

在 Kotlin 語言體系當(dāng)中,是沒有原始類型這個(gè)概念的。這也就意味著,在 Kotlin 里,一切都是對(duì)象。

空安全(Null Safety )

既然 Kotlin 中的一切都是對(duì)象,那么對(duì)象就有可能為空。如果我寫這樣的代碼:

val i: Double = null // 編譯器報(bào)錯(cuò)

以上的代碼并不能通過 Kotlin 編譯。


這是因?yàn)?Kotlin 強(qiáng)制要求開發(fā)者在定義變量的時(shí)候,指定這個(gè)變量是否可能為 null

對(duì)于可能為 null 的變量,我們需要在聲明的時(shí)候,在變量類型后面加一個(gè)問號(hào)“?”:

val i: Double = null // 編譯器報(bào)錯(cuò)
val j: Double? = null // 編譯通過

并且由于 Kotlin 對(duì)可能為空的變量類型做了強(qiáng)制區(qū)分,這就意味著,“可能為空的變量”無法直接賦值給“不可為空的變量”,反過來 “不可為空的變量” 可以賦值給“可能為空的變量” 。

var i: Double = 1.0var j: Double? = nulli = j ?// 編譯器報(bào)錯(cuò)j = i ?// 編譯通過

這么設(shè)計(jì)的原因是,從集合邏輯上:可能為空 包含 不可為空

而如果我們實(shí)在有這樣的需求,也不難實(shí)現(xiàn),只要做個(gè)判斷即可:

var i: Double = 1.0val j: Double? = nullif (j != null) { ? ?i = j ?// 編譯通過}

函數(shù)聲明( Define Function )

在 Kotlin 當(dāng)中,函數(shù)的聲明與 Java 不太一樣。 Java:

? public String helloFunction(@NotNull String name) {
? ? ?return "Hello " + name + " !";
? }

Kotlin :

/*
關(guān)鍵字 ? ?函數(shù)名 ? ? ? ? ?參數(shù)類型 ? 返回值類型
↓ ? ? ? ?↓ ? ? ? ? ? ? ? ?↓ ? ? ? ↓ ? ? ?*/
fun helloFunction(name: String): String {
? ?return "Hello $name !"
}/* ? ↑
? 花括號(hào)內(nèi)為:函數(shù)體
*/

  • 使用了 fun 關(guān)鍵字來定義函數(shù);

  • 返回值類型,緊跟在參數(shù)的后面,這點(diǎn)和 Java 不一樣。

如果函數(shù)體中只有一行代碼,可以簡(jiǎn)寫

  • return可以省略

  • { } 花括號(hào)可以省略

  • 直接用 = 連接,變成一種類似 變量賦值的 函數(shù)形式

fun helloFunton(name:String):String = "Hello $name !"

我們稱之為單表達(dá)式函數(shù)

由于Kotlin支持類型推導(dǎo),返回值類型可以省略:

fun helloFunton(name:String):= "Hello $name !"

這樣看起來就更簡(jiǎn)潔了。

讓函數(shù)更好的調(diào)用( Making functions easier to call )

命名參數(shù)/具名參數(shù) (Named arguments)

以前面的函數(shù)為例子,我們調(diào)用它:

helloFunction("Kotlin")

和 Java 一樣。

不過,Kotlin 提供了一些新的特性,如命名函數(shù)參數(shù) 舉個(gè)例子,現(xiàn)在有一個(gè)函數(shù):

fun createUser( ? ?name: String, ? ?age: Int, ? ?gender: Int, ? ?friendCount: Int, ? ?feedCount: Int, ? ?likeCount: Long, ? ?commentCount: Int) { ? ?//..}

如果像 Java 那樣調(diào)用:

createUser("Tom", 30, 1, 78, 2093, 10937, 3285)

就要嚴(yán)格按照參數(shù)順序傳參:

  • 參數(shù)順序調(diào)換,參數(shù)就傳錯(cuò)了,不好維護(hù)

  • 當(dāng)參數(shù)是一堆數(shù)字,很難知道數(shù)字對(duì)應(yīng)的形參,可讀性不高

Kotlin 參數(shù)調(diào)用:

createUser(
? ?name = "Tom",
? ?age = 30,
? ?gender = 1,
? ?friendCount = 78,
? ?feedCount = 2093,
? ?likeCount = 10937,
? ?commentCount = 3285
)

我們把函數(shù)的形參加了進(jìn)來,形參和實(shí)參用 = 連接,建立了兩者的對(duì)應(yīng)關(guān)系。這樣可讀性更強(qiáng)。

如果想修改某個(gè)參數(shù)例如feedCount也可以很方便的定位到參數(shù)。 這樣易維護(hù)

參數(shù)默認(rèn)值(Default arguments)

fun createUser(
? ?name: String,
? ?age: Int,
? ?gender: Int = 1,
? ?friendCount: Int = 0,
? ?feedCount: Int = 0,
? ?likeCount: Long = 0L,
? ?commentCount: Int = 0
) {
? ?//..
}

gender、likeCount 等參數(shù)被賦予了默認(rèn)值,當(dāng)我們調(diào)用時(shí),有些有默認(rèn)值的參數(shù)就可以不傳參,Kotlin編譯器自動(dòng)幫我們填上默認(rèn)值。

createUser( ? ?name = "Tom", ? ?age = 30, ? ?friendCount = 50)

在 Java 當(dāng)中要實(shí)現(xiàn)類似的邏輯,我們就必須手動(dòng)定義新的“3 個(gè)參數(shù)的 createUser 函數(shù)”,或者是使用 Builder 設(shè)計(jì)模式。


Classes and Objects

  • 類 (Class)

  • 抽象類 (Abstract Class)

  • 繼承(Extend)

  • 接口和實(shí)現(xiàn) (Interface and implements)

  • 嵌套類和內(nèi)部類( Nested and Inner Classes )

  • 數(shù)據(jù)類(Data Class )

  • object 關(guān)鍵字

    • object:匿名內(nèi)部類

    • object:?jiǎn)卫J?/span>

    • object:伴生對(duì)象

  • 擴(kuò)展 (Extension)

    • 什么是擴(kuò)展函數(shù)和擴(kuò)展屬性?

    • 擴(kuò)展函數(shù)在 Android 中的案例

類 (Class)

Java

public class Person { ? ?private String name; ? ?private int age; ? ?public Person(String name, int age) { ? ? ? ?this.name = name; ? ? ? ?this.age = age; ? ?} ? ?// 屬性 name 沒有 setter ? ?public String getName() { ? ? ? ?return name; ? ?} ? ?public int getAge() { ? ? ? ?return age; ? ?} ? ?public void setAge(int age) { ? ? ? ?this.age = age; ? ?}}


Class

Kotlin

class Person(val name: String, var age: Int)

Kotlin 定義類,同樣使用 class 關(guān)鍵字。 ? ? Kotlin 定義的類在默認(rèn)情況下是 public 的。 ? ? 編譯器會(huì)幫我們生成“構(gòu)造函數(shù)”,

對(duì)于類當(dāng)中的屬性,Kotlin 編譯器也會(huì)根據(jù)實(shí)際情況,自動(dòng)生成 getter 和 setter。 ? ? 和Java相比 Kotlin 定義一個(gè)類足夠簡(jiǎn)潔。

抽象類與繼承

抽象類 (Abstract Class)

abstract class Person(val name: String) {
? ?abstract fun walk()
? ?// 省略
}

繼承(Extend)

// ? ? ? ? ? ? ? ? ? ? ?Java 的繼承
// ? ? ? ? ? ? ? ? ? ? ? ? ? ↓
public class MainActivity extends Activity {
? ?@Override
? ?void onCreate(){ ... }
}// ? ? ? ? ? ? ?Kotlin 的繼承
// ? ? ? ? ? ? ? ? ↓
class MainActivity : AppCompatActivity() {
? ?override fun onCreate() { ... }
}

接口和實(shí)現(xiàn) (Interface and implements)

Kotlin 當(dāng)中的接口(interface),和 Java 也是大同小異的,它們都是通過 interface 這個(gè)關(guān)鍵字來定義的。

interface Behavior { ? ?fun walk()}class Person(val name: String): Behavior { ? ?override fun walk() { ? ? ? ?// walk ? ?} ? ?// ...}

可以看到在以上的代碼中,我們定義了一個(gè)新的接口 Behavior,它里面有一個(gè)需要被實(shí)現(xiàn)的方法 walk,然后我們?cè)?Person 類當(dāng)中實(shí)現(xiàn)了這個(gè)接口。

Kotlin 的繼承和接口實(shí)現(xiàn)語法基本上是一樣的。

Kotlin 的接口,跟 Java 最大的差異就在于,接口的方法可以有默認(rèn)實(shí)現(xiàn),同時(shí),它也可以有屬性。

interface Behavior { ? ?// 接口內(nèi)的可以有屬性 ? ?val canWalk: Boolean ? ?// 接口方法的默認(rèn)實(shí)現(xiàn) ? ?fun walk() { ? ? ? ?if (canWalk) { ? ? ? ? ? ?// do something ? ? ? ?} ? ?}}class Person(val name: String): Behavior { ? ?// 重寫接口的屬性 ? ?override val canWalk: Boolean ? ? ? ?get() = true}

我們?cè)诮涌诜椒ó?dāng)中,為 walk() 方法提供了默認(rèn)實(shí)現(xiàn),如果 canWalk 為 true,才執(zhí)行 walk 內(nèi)部的具體行為。

Kotlin 當(dāng)中的接口,被設(shè)計(jì)得更加強(qiáng)大了。

在 Java 1.8 版本當(dāng)中,Java接口也引入了類似的特性。

嵌套類和內(nèi)部類( Nested and Inner Classes )

Java 當(dāng)中,最常見的嵌套類分為兩種:非靜態(tài)內(nèi)部類、靜態(tài)內(nèi)部類。Kotlin 當(dāng)中也有一樣的概念。

class A {
? ?class B {
? ?}
}

以上代碼中,B 類,就是 A 類里面的嵌套類。

注意: ? 無法在 B 類當(dāng)中訪問 A 類的屬性和成員方法。

因?yàn)镵otlin 默認(rèn)嵌套類(B類)是一個(gè)靜態(tài)內(nèi)部類


Kotlin 嵌套類反編譯成 Java 代碼:

public class JavaOuterInnerClass2 { ? // 內(nèi)部類 ? ?public ?class InnerClass { ? ?} ? ?// 靜態(tài)內(nèi)部類 ? ?public ?static ?final ? class ?StaticInnerClass{ ? ?}}

通過 javac 命令 編譯成 class 文件后:

  • InnerClass

  • StaticInnerClass


通過.class 可以發(fā)現(xiàn),

$InnerClass 持有外部類的引用。

$StaticInnerClass 不持有外部類的引用。

Java 當(dāng)中的嵌套類,默認(rèn)情況下,沒有 static關(guān)鍵字 時(shí),它就是一個(gè)內(nèi)部類,這樣的內(nèi)部類是會(huì)持有外部類的引用的。 所以,這樣的設(shè)計(jì)在 Java 當(dāng)中會(huì)非常容易出現(xiàn)內(nèi)存泄漏! 而我們之所以會(huì)犯這樣的錯(cuò)誤,往往只是因?yàn)橥浖?code>static關(guān)鍵字。

Kotlin 則恰好相反,在默認(rèn)情況下,嵌套類變成了靜態(tài)內(nèi)部類,而這種情況下的嵌套類是不會(huì)持有外部類引用的。只有當(dāng)我們真正需要訪問外部類成員的時(shí)候,我們才會(huì)加上 inner 關(guān)鍵字。這樣一來,默認(rèn)情況下,開發(fā)者是不會(huì)犯錯(cuò)的,只有手動(dòng)加上 inner 關(guān)鍵字之后,才可能會(huì)出現(xiàn)內(nèi)存泄漏,而當(dāng)我們加上 inner 之后,其實(shí)往往也就能夠意識(shí)到內(nèi)存泄漏的風(fēng)險(xiǎn)了。

數(shù)據(jù)類(Data Class )

Koltin 數(shù)據(jù)類 ,就是用于存放數(shù)據(jù)的類,等價(jià)于 POJO (Plain Ordinary Java Object)。要定義一個(gè)數(shù)據(jù)類,我們只需要在普通的類前面加上一個(gè)關(guān)鍵字 data,就可以把它變成一個(gè)"數(shù)據(jù)類"。

? ?// 數(shù)據(jù)類當(dāng)中,最少要有一個(gè)屬性 ? ? ? ? ? ? ? ? ? ↓data class Person(val name: String, val age: Int)

編譯器會(huì)為數(shù)據(jù)類自動(dòng)生成一些 POJO 常用的方法

  • getter()

  • setter()

  • equals();

  • hashCode();

  • toString();

  • componentN() 函數(shù);

  • copy()。

Koltin 數(shù)據(jù)類反編譯成 Java代碼:

object 關(guān)鍵字

fun 關(guān)鍵字代表了定義函數(shù),class 關(guān)鍵字代表了定義類,這些都是固定的,object 關(guān)鍵字,卻有三種迥然不同的語義,分別可以定義:

  • 匿名內(nèi)部類;

  • 單例模式;

  • 伴生對(duì)象。

之所以會(huì)出現(xiàn)這樣的情況,是因?yàn)?Kotlin 的設(shè)計(jì)者認(rèn)為:

這三種語義本質(zhì)上都是在定義一個(gè)類的同時(shí)還創(chuàng)建了對(duì)象

在這樣的情況下,與其分別定義三種不同的關(guān)鍵字,還不如將它們統(tǒng)一成 object 關(guān)鍵字。

object:匿名內(nèi)部類

在 Java 開發(fā)當(dāng)中,我們經(jīng)常需要寫類似這樣的代碼:

?public interface Runnable { ? ? ?void run(); ?} ?public static void main(String[] args) { ? ? ?// 創(chuàng)建Runnable對(duì)象并使用匿名內(nèi)部類重寫run方法 ? ? ?Runnable runnable = new Runnable() { ? ? ? ? ?public void run() { ? ? ? ? ? ? ?System.out.println("Runnable is running"); ? ? ? ? ?} ? ? ?}; ? ? ?// 創(chuàng)建Thread對(duì)象并將Runnable作為參數(shù)傳入 ? ? ?Thread thread = new Thread(runnable); ? ? ?// 啟動(dòng)線程 ? ? ?thread.start(); ?}

這是典型的匿名內(nèi)部類寫法。


在 Kotlin 當(dāng)中,我們會(huì)使用 object 關(guān)鍵字來創(chuàng)建匿名內(nèi)部類。

? interface Runnable { ? ? ? ?fun run() ? ?} ? ? ? ?@JvmStatic ? ?fun main(args: Array<String>) { ? ? ? ?// 創(chuàng)建Runnable對(duì)象并使用匿名內(nèi)部類重寫run方法 ? ? ? ?val runnable: Runnable = object : Runnable { ? ? ? ? ? ?override fun run() { ? ? ? ? ? ? ? ?println("Runnable is running") ? ? ? ? ? ?} ? ? ? ?} ? ? ? ?// 創(chuàng)建Thread對(duì)象并將Runnable作為參數(shù)傳入 ? ? ? ?val thread: Thread = Thread(runnable) ? ? ? ?// 啟動(dòng)線程 ? ? ? ?thread.start() ? ?}


object:單例模式

在 Kotlin 當(dāng)中,要實(shí)現(xiàn)單例模式其實(shí)非常簡(jiǎn)單,我們直接用 object 修飾類即可:

object UserManager { ? ?fun login() {}}

可以看出,Kotlin 生成單例,代碼量非常少


反編譯后的 Java 代碼:

public final class UserManager { ? public static final UserManager INSTANCE; ?? static { ? ? ?UserManager var0 = new UserManager(); ? ? ?INSTANCE = var0; ? } ? private UserManager() {} ? public final void login() {}}

Kotlin 編譯器會(huì)將其轉(zhuǎn)換成靜態(tài)代碼塊的單例模式。

雖然具有簡(jiǎn)潔的優(yōu)點(diǎn),但同時(shí)也存在兩個(gè)缺點(diǎn)。

  • 不支持懶加載。

  • 不支持傳參構(gòu)造單例。


object:伴生對(duì)象

Kotlin 當(dāng)中沒有 static 關(guān)鍵字,所以我們沒有辦法直接定義靜態(tài)方法和靜態(tài)變量。不過,Kotlin 還是為我們提供了伴生對(duì)象,來幫助實(shí)現(xiàn)靜態(tài)方法和變量。

Kotlin 伴生:

? ?companion object { ? ? ? ?const val LEARNING_FRAGMENT_INDEX = 0 ? ? ? ?? ? ? ?fun jumpToMe(context: Context, index: Int) { ? ? ? ? ? ?context.startActivity(Intent(context, TrainingHomeActivity::class.java).apply { ? ? ? ? ? ? ? ?putExtra(FRAGMENT_INDEX, index) ? ? ? ? ? ?}) ? ? ? ?} ? ?}


反編譯后的 Java 代碼:

? private Companion() { } ? public static final Companion Companion = new Companion((DefaultConstructorMarker)null); ? ?? public static final int LEARNING_FRAGMENT_INDEX = 0; ? ? public static final class Companion { ? ? ?public final void jumpToMe(@NotNull Context context, int index) { ? ? ? ? ? ?} }

可以看到j(luò)umpToMe()并不是靜態(tài)方法,它實(shí)際上是通過調(diào)用單例 Companion 的實(shí)例上的方法實(shí)現(xiàn)的。

擴(kuò)展 (Extension)

Kotlin 的擴(kuò)展(Extension),主要分為兩種語法:

第一個(gè)是擴(kuò)展函數(shù),

第二個(gè)是擴(kuò)展屬性

從語法上看,擴(kuò)展看起來就像是我們從類的外部為它擴(kuò)展了新的成員。

場(chǎng)景:假如我們想修改 JDK 當(dāng)中的 String,想在它的基礎(chǔ)上增加一個(gè)方法“l(fā)astElement()”來獲取末尾元素,如果使用 Java,我們是無法通過常規(guī)手段實(shí)現(xiàn)的,因?yàn)槲覀儧]辦法修改 JDK 的源代碼。任何第三方提供的 SDK,我們都無權(quán)修改。

不過,借助 Kotlin 的擴(kuò)展函數(shù),我們就完全可以在語義層面,來為第三方 SDK 的類擴(kuò)展新的成員方法和成員屬性。


擴(kuò)展函數(shù)

擴(kuò)展函數(shù),就是從類的外部擴(kuò)展出來的一個(gè)函數(shù),這個(gè)函數(shù)看起來就像是類的成員函數(shù)一樣

Extension.kt /* ① ? ?② ? ? ?③ ? ? ? ? ? ?④ ↓ ? ? ↓ ? ? ? ↓ ? ? ? ? ? ?↓ ? */ ? ? fun String.lastElement(): Char? { ? ?// ? ⑤ ? ?// ? ↓ ? ?if (this.isEmpty()) { ? ? ? ?return null ? ?} ? ?return this[length - 1]}// 使用擴(kuò)展函數(shù)fun main() { ? ?val msg = "Hello Wolrd" ? ?// lastElement就像String的成員方法一樣可以直接調(diào)用 ? ?val last = msg.lastElement() // last = d}


  • 注釋①,fun關(guān)鍵字,代表我們要定義一個(gè)函數(shù)。也就是說,不管是定義普通 Kotlin 函數(shù),還是定義擴(kuò)展函數(shù),我們都需要 fun 關(guān)鍵字。

  • 注釋②,“String.”,代表我們的擴(kuò)展函數(shù)是為 String 這個(gè)類定義的。在 Kotlin 當(dāng)中,它有一個(gè)名字,叫做接收者(Receiver),也就是擴(kuò)展函數(shù)的接收方。

  • 注釋③,lastElement(),是我們定義的擴(kuò)展函數(shù)的名稱。

  • 注釋④,“Char?”,代表擴(kuò)展函數(shù)的返回值是可能為空的 Char 類型。

  • 注釋⑤,“this.”,代表“具體的 String 對(duì)象”,當(dāng)我們調(diào)用 msg.lastElement() 的時(shí)候,this 就代表了 msg。

擴(kuò)展函數(shù)反編譯成 Java 代碼:

public final class StringExtKt {
? @Nullable
? public static final Character lastElement(@NotNull String $this$lastElement) {
? ? ?// 省略
? }
}

而如果我們將上面的 StringExtKt 修改成 StringUtils,它就變成了典型的 Java 工具類

public final class StringUtils {

? public static final Character lastElement(String $this) {
? ? // 省略
? }
}
public static final void main() {
?Character last = StringUtils.lastElement(msg);
}

所以 Kotlin 擴(kuò)展函數(shù) 本質(zhì) 上和 ?Java靜態(tài)方法 是一樣的。

只是編譯器幫我們做了很多事情, 讓代碼寫起來更簡(jiǎn)潔。

擴(kuò)展屬性

而擴(kuò)展屬性,則是在類的外部為它定義一個(gè)新的成員屬性。

// 接收者類型// ? ? ↓val String.lastElement: Char? ? ?get() = if (isEmpty()) { ? ? ? ? ? ?null ? ? ? ?} else { ? ? ? ? ? ?get(length - 1) ? ? ? ?}fun main() { ? ?val msg = "Hello Wolrd" ? ?// lastElement就像String的成員屬性一樣可以直接調(diào)用 ? ?val last = msg.lastElement // last = d}

擴(kuò)展函數(shù)/擴(kuò)展屬性對(duì)比

轉(zhuǎn)換成Java代碼后,擴(kuò)展函數(shù)和擴(kuò)展屬性代碼一致,

StringUtils.lastElement(msg); } 用法是一樣的。

擴(kuò)展最主要的用途,就是用來取代 Java 當(dāng)中的各種工具類,比如StringUtils、DateUtils 等等。

擴(kuò)展函數(shù)在 Android 中的案例

用擴(kuò)展函數(shù)簡(jiǎn)化Toast的用法:

這是Toast的標(biāo)準(zhǔn)用法,在界面上彈出一段文字提示,代碼很長(zhǎng)。

Toast.makeText(context, "This is Toast",Toast.LENGTH_SHORT).show()

還容易忘記調(diào)show()函數(shù),造成Toast 沒有彈出。

用擴(kuò)展函數(shù)改寫后:

fun String.showToast(context: Context) { ?
? ?Toast.makeText(context, this, Toast.LENGTH_SHORT).show()
}

調(diào)用時(shí),只需要在要展示的內(nèi)容后面調(diào)一下showToast(),這樣就簡(jiǎn)潔了很多。

"This is Toast".showToast(context)

函數(shù)與 Lambda 表達(dá)式

  • 函數(shù)類型(Function Type)

  • 函數(shù)引用 (Function reference)

  • 高階函數(shù)(Higher-order function)

  • 匿名函數(shù) (Anonymous function)

  • Lambda Expressions

  • 函數(shù)式(SAM)接口

  • SAM 轉(zhuǎn)換

  • 高階函數(shù)應(yīng)用

函數(shù)類型(Function Type)

函數(shù)類型(Function Type)就是函數(shù)的類型, 在 Kotlin 的世界里,函數(shù)是一等公民 既然變量可以有類型,函數(shù)也可以有類型。

// ? ? ? ? (Int, ?Int) ->Float 這就是 add 函數(shù)的類型// ? ? ? ? ? ↑ ? ? ↑ ? ? ?↑fun add(a: Int, b: Int): Float { return (a+b).toFloat() }

將第三行代碼里的“ Int Int Float”抽出來,就可以確定該函數(shù)的類型。

將函數(shù)的“參數(shù)類型”和“返回值類型”抽象出來后,加上(),->符號(hào)加工后,就得到了“函數(shù)類型”。

(Int, Int) ->Float 就代表了參數(shù)類型是兩個(gè) Int,返回值類型為 Float 的函數(shù)類型。

函數(shù)引用(Function reference)

普通的變量有引用的概念,我們可以將一個(gè)變量賦值給另一個(gè)變量,這一點(diǎn),在函數(shù)上也是同樣適用的,函數(shù)也有引用,并且也可以賦值給變量。

前面定義的 add 函數(shù),賦值給另一個(gè)函數(shù)變量時(shí),不能直接用的,

需要使用::操作符 , 后跟要引用的函數(shù)名,獲得函數(shù)引用后才可以去賦值。

fun add(a: Int, b: Int): Float { return (a+b).toFloat() }// ? 變量 ? ? 函數(shù)類型 ? ? ? ? ? ? ? 函數(shù)引用 ? ? ? ?// ? ?↑ ? ? ? ? ↑ ? ? ? ? ? ? ? ? ? ? ↑val function: (Int, Int) -> Float = ::add println(function(2, 3)) // 輸出 5

加了雙冒號(hào):: , 這個(gè)函數(shù)才變成了一個(gè)對(duì)象,只有對(duì)象才能被賦值給變量。


fun add(a: Int, b: Int): Float { return (a+b).toFloat() } ?? ?? fun testGaojie() { ? ? println( ::add ) ? ? println( (::add)(2, 3) )// 輸出 5.0 ? ?}

通過反編譯成 Java 代碼,可以看出。

::add 等價(jià)于 ?Function2 var1 = new Function2(...)

是一個(gè)FunctionN 類型的對(duì)象。


反編譯成 Java代碼:

public final void testGaojie() { // ?println( ::add ) ? ? ?Function2 var1 = new Function2((GaojieFunTest)this) { ? ? ? ? public Object invoke(Object var1, Object var2) { ? ? ? ? ? ?return this.invoke(((Number)var1).intValue(), ((Number)var2).intValue()); ? ? ? ? } ? ? ? ? public final float invoke(int p1, int p2) { ? ? ? ? ? ?return ((GaojieFunTest)this.receiver).add(p1, p2); ? ? ? ? } ? ? ?}; ? ? ?System.out.println(var1);// ?println( (::add)(2, 3) ) ? ? ?float var2 = ((Number)((Function2)(new Function2((GaojieFunTest)this) { ? ? ? ? public Object invoke(Object var1, Object var2) { ? ? ? ? ? ?return this.invoke(((Number)var1).intValue(), ((Number)var2).intValue()); ? ? ? ? } ? ? ? ? public final float invoke(int p1, int p2) { ? ? ? ? ? ?return ((GaojieFunTest)this.receiver).add(p1, p2); ? ? ? ? } ? ? ?})).invoke(2, 3)).floatValue(); ? ? ?System.out.println(var2); ? } ? fun add(a: Int, b: Int): Float { return (a+b).toFloat() }
?
? fun testGaojie() {
? ? println( ?add(2, 3) ?)// 輸出 5.0
? ? val function: (Int, Int) -> Float = ::add
? ? println( function(2, 3) ) // 輸出 5.0
? ? println( ?function.invoke(2, 3) ?) ?// 輸出 5.0
? ?}
? ?

將 testGaojie()轉(zhuǎn)換成 Java 代碼。可以看到在 Java 里, 函數(shù)類型被聲明為普通的接口:一個(gè)函數(shù)類型的變量是FunctionN接口的一個(gè)實(shí)現(xiàn)。Kotlin標(biāo)準(zhǔn)庫定義了一系列的接口,這些接口對(duì)應(yīng)于不同參數(shù)數(shù)量函數(shù)Function0<R>(沒有參數(shù)的函數(shù))、Function2<P1,P2,R>(2個(gè)參數(shù)的函數(shù))...Function22<P1,P2 ... R>。每個(gè)接口定義了一個(gè)invoke()方法,調(diào)用這個(gè)方法就會(huì)執(zhí)行函數(shù)。一個(gè)函數(shù)類型的變量就是實(shí)現(xiàn)了對(duì)應(yīng)的FunctionN接口的實(shí)現(xiàn)類實(shí)例。實(shí)現(xiàn)類的invoke()方法包含了 函數(shù)引用對(duì)應(yīng)的函數(shù)函數(shù)體


反編譯成 Java代碼:

public final void testGaojie() { // println( ?add(2, 3) ?) ? ? ?float var1 = this.add(2, 3); ? ? ?System.out.println(var1);// ?val function: (Int, Int) -> Float = ::add ? ? ?? ? ?Function2 function = (Function2)(new Function2((GaojieFunTest)this) { ? ? ? ? // $FF: synthetic method ? ? ? ? // $FF: bridge method ? ? ? ? public Object invoke(Object var1, Object var2) { ? ? ? ? ? ?return this.invoke(((Number)var1).intValue(), ((Number)var2).intValue()); ? ? ? ? } ? ? ? ? public final float invoke(int p1, int p2) { ? ? ? ? ? ?return ((GaojieFunTest)this.receiver).add(p1, p2); ? ? ? ? } ? ? ?});// println( function(2, 3) ) // 輸出 5.0 ? ? ? ? ? ?float var2 = ((Number)function.invoke(2, 3)).floatValue(); ? ? ?System.out.println(var2);// ?println( ?function.invoke(2, 3) ?) ?// 輸出 5.0 ? ? ?? ? ?var2 = ((Number)function.invoke(2, 3)).floatValue(); ? ? ?System.out.println(var2); ? }

總結(jié)

Kotlin中,函數(shù)引用和函數(shù)調(diào)用有以下區(qū)別:

  1. 函數(shù)引用可以視為函數(shù)類型的變量,它持有函數(shù)的引用。而函數(shù)調(diào)用則執(zhí)行函數(shù)本身。因此,可以將函數(shù)引用傳遞給其他函數(shù),并在需要時(shí)執(zhí)行。

  2. 函數(shù)引用可以簡(jiǎn)化調(diào)用代碼,避免冗長(zhǎng)的代碼。而函數(shù)調(diào)用則需要編寫完整的函數(shù)名稱、參數(shù)和參數(shù)類型。

  3. 函數(shù)引用不會(huì)立即執(zhí)行函數(shù)代碼,只有在需要時(shí)才執(zhí)行。而函數(shù)調(diào)用則立即執(zhí)行函數(shù)代碼。 例如,假設(shè)我們有一個(gè)名為“double”的函數(shù),它接受一個(gè)整數(shù)并返回它的兩倍。那么,函數(shù)引用和函數(shù)調(diào)用的代碼如下所示:

val doubleFunc: (Int) -> Int = ::double // 函數(shù)調(diào)用val result = double(5) // 返回 10

在這個(gè)例子中,我們定義了一個(gè)函數(shù)引用,它可以在需要時(shí)傳遞給其他函數(shù),也可以在需要時(shí)執(zhí)行。

第 2 行代碼我們還調(diào)用了函數(shù)“double”,它立即執(zhí)行代碼并返回結(jié)果。

高階函數(shù) (Higher-order function)

高階函數(shù)的定義:高階函數(shù)是將函數(shù)用作參數(shù)或者返回值的函數(shù)。

如果一個(gè)函數(shù)的參數(shù)類型函數(shù)類型或者返回值類型函數(shù)類型,那么這個(gè)函數(shù)就是就是高階函數(shù) 。

或者說,如果一個(gè)函數(shù)的參數(shù)或者返回值,其中有一個(gè)是函數(shù),那么這個(gè)函數(shù)就是高階函數(shù)。

? ?// ? ? ? ? ? ? ? ? ? ? ? ? ? ?函數(shù)類型的變量 ? 函數(shù)類型 ? ?// ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ↓ ? ? ? ? ? ?↓ ? ?fun ?higherOrderAdd( a:Int,b: Int,block: (Int, Int) -> Float):Float{// ? ? ? ? ? ? ? ? ? 函數(shù)類型的變量// ? ? ? ? ? ? ? ? ? ? ? ↓ ? ? ? ?var ?result = block.invoke(a,b) // ? ? ? ? ? ? ? ? ? 函數(shù)類型的變量// ? ? ? ? ? ? ? ? ? ? ? ↓ ? ? ? ?var ?result2 = block(a,b) ? ? ? ?println("result:$result") ? ? ? ?return result ? ?}

higherOrderAdd 有一個(gè)參數(shù)是函數(shù)類型,所以它是高階函數(shù)

匿名函數(shù)

匿名函數(shù)看起來跟普通函數(shù)很相似,除了它的名字參數(shù)類型被省略了外。 匿名函數(shù)示例如下:

fun(a :Int, b :Int) = a + b

上面的匿名函數(shù)是沒法直接調(diào)用的,賦值給變量后才可以調(diào)用

val anonymousFunction = fun(a :Int, b :Int) = a + b ?fun anonymousFunctionTest() { ? ? ? ?higherOrderAdd(2,2,::add) // 函數(shù)引用 ? ? ? ?higherOrderAdd(2,2,anonymousFunction) // 函數(shù)變量 ? ? ? ?higherOrderAdd(2,2, ? ? ? ? ? ?fun (a:Int,b:Int):Float{ return (a+b).toFloat()}) // 匿名函數(shù) ? ?}

匿名函數(shù)本質(zhì)上也是函數(shù)類型的對(duì)象,所以可以賦值給變量。



匿名函數(shù)不能單獨(dú)聲明在 ()外面,因?yàn)槟涿瘮?shù)是(函數(shù)的聲明函數(shù)引用合二為一)

// 具名函數(shù)不能直接賦值給變量,因?yàn)樗皇菍?duì)象

// 函數(shù)()內(nèi)不能直接 聲明 具名函數(shù),因?yàn)樗皇菍?duì)象


這幾個(gè)個(gè)報(bào)錯(cuò)是因?yàn)?,匿名函?shù)是把函數(shù)的聲明函數(shù)引用合二為一了,所以在需要匿名函數(shù)的地方,聲明一個(gè)具名函數(shù)是報(bào)錯(cuò)的,正確的做法是改用具名函數(shù)引用 例如:

?higherOrderAdd(2,2,::add) // 函數(shù)引用


Lambda

Java 在 Java8中引入的Lambda。

Java Lambda 的基本語法是

(parameters) -> expression

或(請(qǐng)注意語句的花括號(hào))

?(parameters) -> { statements; }



Kotlin 語言的是可以用 Lambda 表達(dá)式作為函數(shù)參數(shù)的,Lambda就是一小段可以作為參數(shù)傳遞的代碼,那么到底多少代碼才算一小段代碼呢?Kotlin對(duì)此并沒有進(jìn)行限制,但是通常不建議在Lambda 表達(dá)式中編寫太長(zhǎng)的代碼,否則可能會(huì)影響代碼的可讀性

Lambda也可以理解為是匿名函數(shù)簡(jiǎn)寫。

我們來看一下Lambda表達(dá)式的語法結(jié)構(gòu):

{參數(shù)名1: 參數(shù)類型, 參數(shù)名2: 參數(shù)類型 -> 函數(shù)體}

首先最外層是一對(duì)花括號(hào){ },如果有參數(shù)傳入到Lambda表達(dá)式中的話,我們還需要聲明參數(shù)列表,參數(shù)列表的結(jié)尾使用一個(gè) '->' 符號(hào) ,表示參數(shù)列表的結(jié)束以及函數(shù)體的開始,函數(shù)體中可以編寫任意行代碼,并且最后一行代碼會(huì)自動(dòng)作為L(zhǎng)ambda表達(dá)式的返回值。

? ?fun ?higherOrderAdd( a:Int,b: Int,block: (Int, Int) -> Float):Float{
? ? ? ?var ?result = block(a,b)
? ? ? ?println("result:$result")
? ? ? ?return result
? ?}
? ? ?@Test
? ?fun anonymousFunctionTest() {
? ? ? ?higherOrderAdd(2,2,::add) // 函數(shù)引用
? ? ? ?higherOrderAdd(3,3,
? ? ? ? ? ?fun (a:Int,b:Int):Float{ return (a+b).toFloat()}) // 匿名函數(shù)
? ? ? ?higherOrderAdd(4,4,
? ? ? ? ? ? { a:Int,b:Int -> ?(a+b).toFloat()}) // ? ?Lambda表達(dá)式
? ? ? ?println(
? ? ? ? ? ?fun (a:Int,b:Int):Float{ return (a+b).toFloat()}(5,5) ) // 匿名函數(shù)直接調(diào)用
? ? ? ?println(
? ? ? ? ? ?{ a:Int,b:Int -> ?(a+b).toFloat()}(5,5)) // Lambda表達(dá)式調(diào)用
? ?} ?

相比匿名函數(shù),lambda 表達(dá)式定義與引用函數(shù)更 簡(jiǎn)潔

函數(shù)式(SAM)接口

SAM 是 Single Abstract Method 的縮寫,只有一個(gè)抽象方法的接口稱為函數(shù)式接口SAM(單一抽象方法)接口。函數(shù)式接口可以有多個(gè)非抽象成員,但只能有一個(gè)抽象成員。


在Java 中可以用注解@FunctionalInterface 聲明一個(gè)函數(shù)式接口:

@FunctionalInterface
public interface Runnable {
? ?void run();
}

在 Kotlin 中可以用 fun 修飾符在 Kotlin 中聲明一個(gè)函數(shù)式接口:

// 注意 interface 前的 fun
fun interface KRunnable {
? fun invoke()
}


SAM 轉(zhuǎn)換

對(duì)于函數(shù)式接口,可以通過 lambda 表達(dá)式實(shí)現(xiàn) SAM 轉(zhuǎn)換,從而使代碼更簡(jiǎn)潔、更有可讀性。

使用 lambda 表達(dá)式可以替代手動(dòng)創(chuàng)建 實(shí)現(xiàn)函數(shù)式接口的類。 通過 SAM 轉(zhuǎn)換, Kotlin 可以將 ?簽名與接口的單個(gè)抽象方法的簽名匹配的任何 lambda 表達(dá)式,轉(zhuǎn)換成實(shí)現(xiàn)該接口的類的實(shí)例。

// 注意需用fun 關(guān)鍵字聲明
fun ?interface ?Action{
? ?fun run(str:String)
}
fun ?runAction(action: Action){
? ? action.run("this ?run")
}


fun main() {// ? ? ?創(chuàng)建一個(gè) 實(shí)現(xiàn)函數(shù)式接口 的類 的實(shí)例(匿名內(nèi)部類) ? ?val action = object :Action{ ? ? ? ?override fun run(str: String) { ? ? ? ? ? ?println(str) ? ? ? ?} ? ?} ? ?// ? 傳入實(shí)例,不使用 SAM 轉(zhuǎn)換 ? ?runAction(action)// ? ?利用 Kotlin 的 SAM 轉(zhuǎn)換,可以改為以下等效代碼:// ? ?使用 Lambda表達(dá)式替代手動(dòng)創(chuàng)建 實(shí)現(xiàn)函數(shù)式接口的類 ? ?runAction({ ? ? ? ? ? ?str-> println(str) ? ?})}


fun ?interface ?InterfaceApi{ ? ?fun run(str:String)}fun ?runInterface(interfaceApi: InterfaceApi){ ? ?interfaceApi.run("this ?run")}// ?函數(shù)類型替代接口定義fun ?factionTypeReplaceInterface(block:(String)->Unit){ ? ? block("this block run")}//=======Test====// 普通函數(shù),參數(shù)是函數(shù)式接口對(duì)象,傳 函數(shù)類型對(duì)象 也是可以的fun ?testFactionTypeReplaceInterface(){ ? ?val function:(String)->Unit = { println(it) } ? ?runInterface(function) //普通函數(shù),參數(shù)是函數(shù)式接口對(duì)象,傳 函數(shù)類型對(duì)象 也是可以的 ? ?factionTypeReplaceInterface(function)}// 高階函數(shù), 參數(shù)是函數(shù)類型對(duì)象,傳 是函數(shù)式接口對(duì)象 是不可以的。fun ?testInterface(){ ? ?val interfaceApi:InterfaceApi = object :InterfaceApi{ ? ? ? ?override fun run(str: String) { ? ? ? ? ? ?println(str) ? ? ? ?} ? ?} ? ?runInterface(interfaceApi) ? ?factionTypeReplaceInterface(interfaceApi)// 高階函數(shù), 參數(shù)是函數(shù)類型對(duì)象,傳 是函數(shù)式接口對(duì)象 是不可以的。}



普通函數(shù),參數(shù)是函數(shù)式接口對(duì)象,傳 函數(shù)類型對(duì)象 也是可以的

反過來不可以:

高階函數(shù), 參數(shù)是函數(shù)類型對(duì)象,傳 是函數(shù)式接口對(duì)象 是不可以的。


前面說的都是函數(shù)傳不同的參數(shù)類型。


這張圖中的三處報(bào)錯(cuò)都是,類型不匹配

說明:

作為函數(shù)實(shí)參時(shí), ? 函數(shù)類型對(duì)象 單向代替 函數(shù)式接口對(duì)象。 ?

但是在創(chuàng)建對(duì)象時(shí), ?函數(shù)類型、函數(shù)式接口兩種類型是涇渭分明的。


高階函數(shù)應(yīng)用

在Android開發(fā)時(shí),我們經(jīng)常會(huì)遇到給自定義View綁定點(diǎn)擊事件的場(chǎng)景。以往通常的做法如下:

// CustomView.java// 成員變量private OnContextClickListener mOnContextClickListener;// 監(jiān)聽手指點(diǎn)擊內(nèi)容事件public void setOnContextClickListener(OnContextClickListener l) { ? ?mOnContextClickListener = l;}// 為傳遞這個(gè)點(diǎn)擊事件,專門定義了一個(gè)接口public interface OnContextClickListener { ? ?void onContextClick(View v);}


// 設(shè)置手指點(diǎn)擊事件customView.setOnContextClickListener(new View.OnContextClickListener() { ? ?@Override ? ?public void onContextClick(View v) { ? ? ? ?gotoPreview(); ? ?}});

看完了這兩段代碼之后,你有沒有覺得這樣的代碼會(huì)很啰嗦?因?yàn)?,真正邏輯只有一行代碼:gotoPreview(),而實(shí)際上我們卻寫了 6 行代碼。


用 Kotlin 高階函數(shù) 改寫后

//View.kt// ? ? ? ? ? ? ? ? ? ? (View) -> Unit 就是「函數(shù)類型 」// ? ? ? ? ? ? ? ? ? ? ? ↑ ? ? ? ?↑ var mOnContextClickListener: ((View) -> Unit)? = null// 高階函數(shù)fun setOnContextClickListener(l: (View) -> Unit) { ? ?mOnClickListener = l;}

如果我們將前面Java寫的例子的核心邏輯提取出來,會(huì)發(fā)現(xiàn)這樣才是最簡(jiǎn)單明了的:

// ? ? ? ? ? ? ? ? ? ? ?{ gotoPreview() } 就是 Lambda// ? ? ? ? ? ? ? ? ? ? ? ? ? ? ↑c(diǎn)ustomView.setOnContextClickListener({ gotoPreview() })

Kotlin 語言的設(shè)計(jì)者是怎么做的呢?實(shí)際上他們是分成了兩個(gè)部分:

  • 用函數(shù)類型替代接口定義;

  • 用 Lambda 表達(dá)式作為函數(shù)參數(shù)。

Kotlin 中引入高階函數(shù)會(huì)帶來幾個(gè)好處:一個(gè)是針對(duì)定義方,代碼中減少了接口類的定義;另一個(gè)是對(duì)于調(diào)用方來說,代碼也會(huì)更加簡(jiǎn)潔。這樣一來,就大大減少了代碼量,提高了代碼可讀性,并通過減少類的數(shù)量,提高了代碼的性能。


不使用高階函數(shù)使用高階函數(shù)定義方需要額外定義接口不需要額外定義接口調(diào)用方代碼繁瑣代碼簡(jiǎn)潔清晰性能差借助inline的情況,性能更高




最后總結(jié)

思考討論

本文主要分享了 空安全、擴(kuò)展函數(shù)、高階函數(shù)、Lambda,

本文分享的Kotlin內(nèi)容,您認(rèn)為哪些特性是最有趣或最有用的?

參考文檔:

  • Kotlin 語言中文站

  • 《Kotlin實(shí)戰(zhàn)》

  • 《Kotlin核心編程》

  • 《Kotlin編程權(quán)威指南》

  • 《Java 8實(shí)戰(zhàn)》


From Java To Kotlin:空安全、擴(kuò)展、函數(shù)、Lambda很詳細(xì),這次終于懂了的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
普格县| 辰溪县| 体育| 井冈山市| 孟村| 鄯善县| 凌海市| 宣威市| 澄江县| 安岳县| 翁源县| 津市市| 东阳市| 台东市| 嵩明县| 宁乡县| 鹤岗市| 鸡东县| 社旗县| 夹江县| 崇阳县| 泌阳县| 萍乡市| 仙游县| 刚察县| 博野县| 瓦房店市| 湘西| 交城县| 枞阳县| 汨罗市| 桦南县| 上杭县| 内黄县| 古交市| 彭泽县| 昭通市| 天峻县| 汝阳县| 察隅县| 阳高县|