亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > 綜合 > 正文

Kotlin開發筆記之委托屬性與區間(譯)

2024-07-21 23:03:41
字體:
來源:轉載
供稿:網友

前言

本文主要給大家介紹了關于Kotlin委托屬性與區間的相關內容,分享出來供大家參考學習,下面話不多說了,來一起看看詳細的介紹吧。

委托屬性

有一些常見的屬性類型,雖然我們可以在每次需要的時候手動實現它們, 但是如果能夠為大家把他們只實現一次并放入一個庫會更好。例如包括

  • 延遲屬性(lazy properties): 其值只在首次訪問時計算,
  • 可觀察屬性(observable properties): 監聽器會收到有關此屬性變更的通知,
  • 把多個屬性儲存在一個映射(map)中,而不是每個存在單獨的字段中。

為了涵蓋這些(以及其他)情況,Kotlin 支持 委托屬性:

class Example {var p: String by Delegate()}

委托屬性 是一種通過委托實現擁有 getter 和可選 setter 的 屬性,并允許實現可復用的自定義屬性。例如:

class Example { var p: String by Delegate()}

委托對象必須實現一個擁有 getValue() 方法的操作符,以及 setValue() 方法來實現讀/寫屬性。些方法將會接受包含對象實例以及屬性元數據作為額外參數。當一個類聲明委托屬性時,編譯器生成的代碼會和如下 Java 代碼相似。

public final class Example { @NotNull private final Delegate p$delegate = new Delegate(); // $FF: synthetic field static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Reflection.getOrCreateKotlinClass(Example.class), "p", "getP()Ljava/lang/String;"))}; @NotNull public final String getP() { return this.p$delegate.getValue(this, $$delegatedProperties[0]); } public final void setP(@NotNull String var1) { Intrinsics.checkParameterIsNotNull(var1, "<set-?>"); this.p$delegate.setValue(this, $$delegatedProperties[0], var1); }}

一些靜態屬性元數據被加入到類中,委托在類的構造函數中初始化,并在每次讀寫屬性時調用。

委托實例

在上面的例子中,創建了一個新的委托實例來實現屬性。這就要求委托的實現是有狀態的,例如,當其內部緩存計算結果時:

class StringDelegate { private var cache: String? = null operator fun getValue(thisRef: Any?, property: KProperty<*>): String { var result = cache if (result == null) { result = someOperation() cache = result } return result }}

與此同時,當需要額外的參數時,需要建立新的委托實例,并將其傳遞到構造器中。

class Example { private val nameView by BindViewDelegate<TextView>(R.id.name)}

但也有一些情況是只需要一個委托實例來實現任何屬性的:當委托是無狀態,并且它所需要的唯一變量就是已經提供好的包含對象實例和委托名稱時,可以通過將其聲明為 object 來替代 class 實現一個單例委托。

舉個例子,下面的單例委托從 Android Activity 中取回與給定 tag 相匹配的 Fragment。

object FragmentDelegate { operator fun getValue(thisRef: Activity, property: KProperty<*>): Fragment? { return thisRef.fragmentManager.findFragmentByTag(property.name) }}

類似地,任何已有類都可以通過擴展變成委托。getValue()setValue() 也可以被聲明成 擴展方法 來實現。Kotlin 已經提供了內置的擴展方法來允許將 Map and MutableMap 實例用作委托,屬性名作為其中的鍵。

如果你選擇復用相同的局部委托實例來在一個類中實現多屬性,你需要在構造函數中初始化實例。

注意:從 Kotlin 1.1 開始,也可以聲明 方法局部變量聲明為委托屬性。在這種情況下,委托可以直到該變量在方法內部聲明的時候才去初始化,而不必在構造函數中就執行初始化。

泛型委托

委托方法也可以被聲明成泛型的,這樣一來不同類型的屬性就可以復用同一個委托類了。

private var maxDelay: Long by SharedPreferencesDelegate<Long>()

然而,如果像上例那樣對基本類型使用泛型委托的話,即便聲明的基本類型非空,也會在每次讀寫屬性的時候觸發裝箱和拆箱的操作。

說明:對于非空基本類型的委托屬性來說,最好使用給定類型的特定委托類而不是泛型委托來避免每次訪問屬性時增加裝箱的額外開銷。

標準委托:lazy()

針對常見情形,Kotlin 提供了一些標準委托,如 Delegates.notNull() 、 Delegates.observable() lazy() 。

lazy() 是一個在第一次讀取時通過給定的 lambda 值來計算屬性的初值,并返回只讀屬性的委托。

private val dateFormat: DateFormat by lazy { SimpleDateFormat("dd-MM-yyyy", Locale.getDefault())}

這是一種簡潔的延遲高消耗的初始化至其真正需要時的方式,在保留代碼可讀性的同時提升了性能。

需要注意的是lazy() 并不是內聯函數,傳入的 lambda 參數也會被編譯成一個額外的 Function 類,并且不會被內聯到返回的委托對象中。

經常被忽略的一點是 lazy() 有可選的 mode 參數 來決定應該返回 3 種委托的哪一種:

public fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)public fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> = when (mode) { LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer) LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer) LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer) }

默認模式 LazyThreadSafetyMode.SYNCHRONIZED 將提供相對耗費昂貴的 雙重檢查鎖 來保證一旦屬性可以從多線程讀取時初始化塊可以安全地執行。

如果你確信屬性只會在單線程(如主線程)被訪問,那么可以選擇 LazyThreadSafetyMode.NONE 來代替,從而避免使用鎖的額外開銷。

val dateFormat: DateFormat by lazy(LazyThreadSafetyMode.NONE) { SimpleDateFormat("dd-MM-yyyy", Locale.getDefault())}

區間

區間 是 Kotlin 中用來代表一個有限的值集合的特殊表達式。值可以是任何 Comparable 類型。 這些表達式的形式都是創建聲明了 ClosedRange 接口的方法。創建區間的主要方法是 .. 操作符方法。

包含

區間表達式的主要作用是使用 in 和 !in 操作符實現包含和不包含。

if (i in 1..10) { println(i)}

該實現針對非空基本類型的區間(包括 Int、Long、Byte、Short、Float、Double 以及 Char 的值)實現了優化,所以上面的代碼可以被優化成這樣:

if(1 <= i && i <= 10) { System.out.println(i);}

零額外支出并且沒有額外對象開銷。區間也可以被包含在 when 表達式中:

val message = when (statusCode) { in 200..299 -> "OK" in 300..399 -> "Find it somewhere else" else -> "Oops"}

相比一系列的 if{…} else if{…} 代碼塊,這段代碼在不降低效率的同時提高了代碼的可讀性。然而,如果在聲明和使用之間有至少一次間接調用的話,range 會有一些微小的額外開銷。

比如下面的代碼:

private val myRange get() = 1..10fun rangeTest(i: Int) { if (i in myRange) { println(i) }}

在編譯后會創建一個額外的 IntRange 對象:

private final IntRange getMyRange() { return new IntRange(1, 10);}public final void rangeTest(int i) { if(this.getMyRange().contains(i)) { System.out.println(i); }}

將屬性的 getter 聲明為 inline 的方法也無法避免這個對象的創建。這是 Kotlin 1.1 編譯器可以優化的一個點。至少通過這些特定的區間類避免了裝箱操作。

說明:盡量在使用時直接聲明非空基本類型的區間,不要間接調用,來避免額外區間類的創建?;蛘咧苯勇暶鳛槌A縼韽陀谩?/p>

區間也可以用于其他實現了 Comparable 的非基本類型。

if (name in "Alfred".."Alicia") { println(name)}

在這種情況下,最終實現并不會優化,而且總是會創建一個 ClosedRange 對象,如下面編譯后的代碼所示:

if(RangesKt.rangeTo((Comparable)"Alfred", (Comparable)"Alicia") .contains((Comparable)name)) { System.out.println(name);}

迭代:for 循環

整型區間 (除了 Float 和 Double之外其他的基本類型)也是 級數:它們可以被迭代。這就可以將經典 Java 的 for 循環用一個更短的表達式替代。

for (i in 1..10) { println(i)}

經過編譯器優化后的代碼實現了零額外開銷:

int i = 1;byte var3 = 10;if(i <= var3) { while(true) { System.out.println(i); if(i == var3) { break; } ++i; }}

如果要反向迭代,可以使用 downTo() 中綴方法來代替 ..:

for (i in 10 downTo 1) { println(i)}

編譯之后,這也實現了零額外開銷:

int i = 10;byte var3 = 1;if(i >= var3) { while(true) { System.out.println(i); if(i == var3) { break; } --i; }}

然而,其他迭代器參數并沒有如此好的優化。反向迭代還有一種結果相同的方式,使用 reversed() 方法結合區間:

for (i in (1..10).reversed()) { println(i)}

編譯后的代碼并沒有看起來那么少:

IntProgression var10000 = RangesKt.reversed((IntProgression)(new IntRange(1, 10)));int i = var10000.getFirst();int var3 = var10000.getLast();int var4 = var10000.getStep();if(var4 > 0) { if(i > var3) { return; }} else if(i < var3) { return;}while(true) { System.out.println(i); if(i == var3) { return; } i += var4;}

會創建一個臨時的 IntRange 對象來代表區間,然后創建另一個 IntProgression 對象來反轉前者的值。

事實上,任何結合不止一個方法來創建遞進都會生成類似的至少創建兩個微小遞進對象的代碼。

這個規則也適用于使用 step() 中綴方法來操作遞進的步驟,即使只有一步:

for (i in 1..10 step 2) { println(i)}

一個次要提示,當生成的代碼讀取 IntProgression 的 last 屬性時會通過對邊界和步長的小小計算來決定準確的最后值。在上面的代碼中,最終值是 9。

最后,until() 中綴函數對于迭代也很有用,該函數(執行結果)不包含最大值。

for (i in 0 until size) { println(i)}

遺憾的是,編譯器并沒有針對這個經典的包含區間圍優化,迭代器依然會創建區間對象:

IntRange var10000 = RangesKt.until(0, size);int i = var10000.getFirst();int var1 = var10000.getLast();if(i <= var1) { while(true) { System.out.println(i); if(i == var1) {  break; } ++i; }}

這是 Kotlin 1.1 可以提升的另一個點,與此同時,可以通過這樣寫來優化代碼:

for (i in 0..size - 1) { println(i)}

說明:

for 循環內部的迭代,最好只用區間表達式的一個單獨方法來調用 .. 或 downTo() 來避免額外臨時遞進對象的創建。

迭代:forEach()

作為 for 循環的替代,使用區間內聯的擴展方法 forEach() 來實現相似的效果可能更吸引人。

(1..10).forEach { println(it)}

但如果仔細觀察這里使用的 forEach() 方法簽名的話,你就會注意到并沒有優化區間,而只是優化了 Iterable,所以需要創建一個 iterator。下面是編譯后代碼的 Java 形式:

Iterable $receiver$iv = (Iterable)(new IntRange(1, 10));Iterator var1 = $receiver$iv.iterator();while(var1.hasNext()) { int element$iv = ((IntIterator)var1).nextInt(); System.out.println(element$iv);}

這段代碼相比前者更為低效,原因是為了創建一個 IntRange 對象,還需要額外創建 IntIterator。但至少它還是生成了基本類型的值。迭代區間時,最好只使用 for 循環而不是區間上的 forEach() 方法來避免額外創建一個迭代器。

迭代:集合

Kotlin 標準庫提供了內置的 indices 擴展屬性來生成數組和 Collection 的區間。

val list = listOf("A", "B", "C")for (i in list.indices) { println(list[i])}

令人驚訝的是,對這個 indices 的迭代得到了編譯器的優化:

List list = CollectionsKt.listOf(new String[]{"A", "B", "C"});int i = 0;int var2 = ((Collection)list).size() - 1;if(i <= var2) { while(true) { Object var3 = list.get(i); System.out.println(var3); if(i == var2) {  break; } ++i; }}

從上面的代碼中我們可以看到沒有創建 IntRange 對象,列表的迭代是以最高效率的方式運行的。

這適用于數組和實現了 Collection 的類,所以你如果期望相同的迭代器性能的話,可以嘗試在特定的類上使用自己的 indices 擴展屬性。

inline val SparseArray<*>.indices: IntRange get() = 0..size() - 1fun printValues(map: SparseArray<String>) { for (i in map.indices) { println(map.valueAt(i)) }}

但編譯之后,我們可以發現這并沒有那么高效率,因為編譯器無法足夠智能地避免區間對象的產生:

public static final void printValues(@NotNull SparseArray map) { Intrinsics.checkParameterIsNotNull(map, "map"); IntRange var10002 = new IntRange(0, map.size() - 1); int i = var10002.getFirst(); int var2 = var10002.getLast(); if(i <= var2) { while(true) {  Object $receiver$iv = map.valueAt(i);  System.out.println($receiver$iv);  if(i == var2) {  break;  }  ++i; } }}

所以,我會建議你避免聲明自定義的 lastIndex 擴展屬性:

inline val SparseArray<*>.lastIndex: Int get() = size() - 1fun printValues(map: SparseArray<String>) { for (i in 0..map.lastIndex) { println(map.valueAt(i)) }}

說明:當迭代沒有聲明 Collection 的自定義集合 時,直接在 for 循環中寫自己的序列區間而不是依賴方法或屬性來生成區間,從而避免區間對象的創建。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VeVb武林網的支持。


注:相關教程知識閱讀請移步到kotlin教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲成人精品视频| 不卡伊人av在线播放| 日韩成人激情影院| 色悠久久久久综合先锋影音下载| 18性欧美xxxⅹ性满足| 亚洲国产精品成人一区二区| 久久久久久中文字幕| 久热精品视频在线观看| 国产精品综合久久久| 久久久久久久97| 欧美孕妇与黑人孕交| 国产一区二区在线播放| 欧美黄色成人网| 亚洲网在线观看| 日韩av毛片网| 久久91超碰青草是什么| 久久国产精品99国产精| 欧美性猛交xxxx乱大交| 欧美大荫蒂xxx| 午夜精品国产精品大乳美女| 激情懂色av一区av二区av| 亚洲欧美国产一区二区三区| 欧美午夜精品久久久久久人妖| 亚洲999一在线观看www| 日韩成人网免费视频| 精品久久中文字幕久久av| 精品久久久久久久久久久久| 日韩av综合网| 国语自产偷拍精品视频偷| 中文国产亚洲喷潮| 国产精品视频区1| 欧美性猛xxx| 国产精品女主播视频| 日韩视频在线免费| 色婷婷综合成人av| 国产伦精品一区二区三区精品视频| 自拍偷拍亚洲一区| 日韩高清中文字幕| 91极品视频在线| 91av在线精品| 国产在线视频欧美| 成人欧美在线观看| 国产欧美久久一区二区| 欧美丰满片xxx777| 欧美精品日韩www.p站| 国产91色在线|免| 久久久国产精品亚洲一区| 精品综合久久久久久97| 国产免费久久av| 成人黄色免费在线观看| 欧美丰满老妇厨房牲生活| 国产精品扒开腿做爽爽爽视频| 色妞欧美日韩在线| 日韩av一区在线| 国产视频亚洲视频| 国产精品日韩一区| 欧美极品美女电影一区| 国产在线观看精品一区二区三区| 欧美日韩亚洲激情| 国产裸体写真av一区二区| 色狠狠av一区二区三区香蕉蜜桃| 性亚洲最疯狂xxxx高清| 精品中文视频在线| 欧美精品成人91久久久久久久| 91美女片黄在线观| 欧美—级a级欧美特级ar全黄| 黑人狂躁日本妞一区二区三区| 尤物九九久久国产精品的分类| 日韩大片在线观看视频| 亚洲国产成人精品电影| 久久综合久中文字幕青草| 亚洲人成在线免费观看| 在线观看国产精品91| 国产精品久久久| 亚洲人成网7777777国产| 中文字幕日韩欧美精品在线观看| 日韩av在线高清| 亚洲免费福利视频| 亚洲激情电影中文字幕| 国语自产精品视频在免费| 欧美黑人巨大精品一区二区| 亚洲国产精品久久久久秋霞蜜臀| 国产日韩在线看| 国产精品国产福利国产秒拍| 欧美成年人视频网站欧美| 国内自拍欧美激情| 精品久久久久久中文字幕大豆网| 久久久免费精品视频| 国产精品日韩在线播放| 久久九九热免费视频| 日韩一区二区久久久| 日韩精品极品毛片系列视频| 日韩美女免费观看| 亚洲乱码一区二区| www.99久久热国产日韩欧美.com| 欧美夜福利tv在线| 欧美日韩亚洲精品一区二区三区| 欧美最顶级丰满的aⅴ艳星| 91精品国产91久久久久久最新| 亚洲国产一区自拍| 日本国产高清不卡| 亚洲欧洲视频在线| 国产噜噜噜噜久久久久久久久| 国产亚洲欧美日韩美女| 日产精品99久久久久久| 91po在线观看91精品国产性色| 欧美中文字幕在线视频| 欧美成人免费大片| 久久精品青青大伊人av| 午夜免费日韩视频| 精品日韩中文字幕| 91久久国产精品| 91精品国产91久久久| 亚洲第一区在线观看| 国产精品高潮在线| 国产一区二区三区在线| 中文字幕亚洲色图| 欧美又大粗又爽又黄大片视频| 综合av色偷偷网| 日韩一区二区三区xxxx| 欧美激情精品久久久久久久变态| 久久久电影免费观看完整版| 日韩精品久久久久| 欧美大片在线看| 亚洲欧美一区二区激情| 日韩精品欧美国产精品忘忧草| 久久久久久久成人| 在线观看91久久久久久| 在线成人中文字幕| 国产亚洲精品美女久久久久| 日韩av中文字幕在线| 欧美日韩激情小视频| 国产男女猛烈无遮挡91| 亚洲免费电影一区| 色偷偷综合社区| 日韩av资源在线播放| 国产精品亚洲激情| 欧美日韩国产第一页| 色天天综合狠狠色| 国产日韩av高清| 成人xvideos免费视频| 国产91精品视频在线观看| 大量国产精品视频| 欧美成人精品在线| 美女久久久久久久久久久| 亚洲精品98久久久久久中文字幕| 中文字幕亚洲欧美在线| 日韩精品在线观看一区二区| 国产精品ⅴa在线观看h| 日韩日本欧美亚洲| 91精品视频网站| 在线播放精品一区二区三区| 一道本无吗dⅴd在线播放一区| 97免费视频在线播放| 国产精品视频一区国模私拍| 亚洲国产小视频| 欧美日韩国产综合视频在线观看中文| 欧美精品18videosex性欧美| 久久久国产视频91| 成人性生交大片免费看视频直播| 亚洲久久久久久久久久| 国产在线精品一区免费香蕉| 亚洲精品自拍偷拍| 欧美激情亚洲国产|