欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Kotlin開(kāi)發(fā)筆記之委托屬性與區(qū)間(譯)

 更新時(shí)間:2017年12月13日 10:01:53   作者:code_xzh  
最近在學(xué)習(xí)kotlin,發(fā)現(xiàn)了一些比較重要的知識(shí)點(diǎn),所以下面這篇文章主要給大家介紹了關(guān)于Kotlin開(kāi)發(fā)筆記之委托屬性與區(qū)間的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。

前言

本文主要給大家介紹了關(guān)于Kotlin委托屬性與區(qū)間的相關(guān)內(nèi)容,分享出來(lái)供大家參考學(xué)習(xí),下面話不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧。

委托屬性

有一些常見(jiàn)的屬性類型,雖然我們可以在每次需要的時(shí)候手動(dòng)實(shí)現(xiàn)它們, 但是如果能夠?yàn)榇蠹野阉麄冎粚?shí)現(xiàn)一次并放入一個(gè)庫(kù)會(huì)更好。例如包括

  • 延遲屬性(lazy properties): 其值只在首次訪問(wèn)時(shí)計(jì)算,
  • 可觀察屬性(observable properties): 監(jiān)聽(tīng)器會(huì)收到有關(guān)此屬性變更的通知,
  • 把多個(gè)屬性儲(chǔ)存在一個(gè)映射(map)中,而不是每個(gè)存在單獨(dú)的字段中。

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

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

委托屬性 是一種通過(guò)委托實(shí)現(xiàn)擁有 getter 和可選 setter 的 屬性,并允許實(shí)現(xiàn)可復(fù)用的自定義屬性。例如:

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

委托對(duì)象必須實(shí)現(xiàn)一個(gè)擁有 getValue() 方法的操作符,以及 setValue() 方法來(lái)實(shí)現(xiàn)讀/寫屬性。些方法將會(huì)接受包含對(duì)象實(shí)例以及屬性元數(shù)據(jù)作為額外參數(shù)。當(dāng)一個(gè)類聲明委托屬性時(shí),編譯器生成的代碼會(huì)和如下 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);
 }
}

一些靜態(tài)屬性元數(shù)據(jù)被加入到類中,委托在類的構(gòu)造函數(shù)中初始化,并在每次讀寫屬性時(shí)調(diào)用。

委托實(shí)例

在上面的例子中,創(chuàng)建了一個(gè)新的委托實(shí)例來(lái)實(shí)現(xiàn)屬性。這就要求委托的實(shí)現(xiàn)是有狀態(tài)的,例如,當(dāng)其內(nèi)部緩存計(jì)算結(jié)果時(shí):

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
 }
}

與此同時(shí),當(dāng)需要額外的參數(shù)時(shí),需要建立新的委托實(shí)例,并將其傳遞到構(gòu)造器中。

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

但也有一些情況是只需要一個(gè)委托實(shí)例來(lái)實(shí)現(xiàn)任何屬性的:當(dāng)委托是無(wú)狀態(tài),并且它所需要的唯一變量就是已經(jīng)提供好的包含對(duì)象實(shí)例和委托名稱時(shí),可以通過(guò)將其聲明為 object 來(lái)替代 class 實(shí)現(xiàn)一個(gè)單例委托。

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

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

類似地,任何已有類都可以通過(guò)擴(kuò)展變成委托。getValue()setValue() 也可以被聲明成 擴(kuò)展方法 來(lái)實(shí)現(xiàn)。Kotlin 已經(jīng)提供了內(nèi)置的擴(kuò)展方法來(lái)允許將 Map and MutableMap 實(shí)例用作委托,屬性名作為其中的鍵。

如果你選擇復(fù)用相同的局部委托實(shí)例來(lái)在一個(gè)類中實(shí)現(xiàn)多屬性,你需要在構(gòu)造函數(shù)中初始化實(shí)例。

注意:從 Kotlin 1.1 開(kāi)始,也可以聲明 方法局部變量聲明為委托屬性。在這種情況下,委托可以直到該變量在方法內(nèi)部聲明的時(shí)候才去初始化,而不必在構(gòu)造函數(shù)中就執(zhí)行初始化。

泛型委托

委托方法也可以被聲明成泛型的,這樣一來(lái)不同類型的屬性就可以復(fù)用同一個(gè)委托類了。

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

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

說(shuō)明:對(duì)于非空基本類型的委托屬性來(lái)說(shuō),最好使用給定類型的特定委托類而不是泛型委托來(lái)避免每次訪問(wèn)屬性時(shí)增加裝箱的額外開(kāi)銷。

標(biāo)準(zhǔn)委托:lazy()

針對(duì)常見(jiàn)情形,Kotlin 提供了一些標(biāo)準(zhǔn)委托,如 Delegates.notNull() 、 Delegates.observable() lazy() 。

lazy() 是一個(gè)在第一次讀取時(shí)通過(guò)給定的 lambda 值來(lái)計(jì)算屬性的初值,并返回只讀屬性的委托。

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

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

需要注意的是,lazy() 并不是內(nèi)聯(lián)函數(shù),傳入的 lambda 參數(shù)也會(huì)被編譯成一個(gè)額外的 Function 類,并且不會(huì)被內(nèi)聯(lián)到返回的委托對(duì)象中。

經(jīng)常被忽略的一點(diǎn)是 lazy() 有可選的 mode 參數(shù) 來(lái)決定應(yīng)該返回 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)
 }

默認(rèn)模式 LazyThreadSafetyMode.SYNCHRONIZED 將提供相對(duì)耗費(fèi)昂貴的 雙重檢查鎖 來(lái)保證一旦屬性可以從多線程讀取時(shí)初始化塊可以安全地執(zhí)行。

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

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

區(qū)間

區(qū)間 是 Kotlin 中用來(lái)代表一個(gè)有限的值集合的特殊表達(dá)式。值可以是任何 Comparable 類型。 這些表達(dá)式的形式都是創(chuàng)建聲明了 ClosedRange 接口的方法。創(chuàng)建區(qū)間的主要方法是 .. 操作符方法。

包含

區(qū)間表達(dá)式的主要作用是使用 in 和 !in 操作符實(shí)現(xiàn)包含和不包含。

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

該實(shí)現(xiàn)針對(duì)非空基本類型的區(qū)間(包括 Int、Long、Byte、Short、Float、Double 以及 Char 的值)實(shí)現(xiàn)了優(yōu)化,所以上面的代碼可以被優(yōu)化成這樣:

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

零額外支出并且沒(méi)有額外對(duì)象開(kāi)銷。區(qū)間也可以被包含在 when 表達(dá)式中:

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

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

比如下面的代碼:

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

在編譯后會(huì)創(chuàng)建一個(gè)額外的 IntRange 對(duì)象:

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 的方法也無(wú)法避免這個(gè)對(duì)象的創(chuàng)建。這是 Kotlin 1.1 編譯器可以優(yōu)化的一個(gè)點(diǎn)。至少通過(guò)這些特定的區(qū)間類避免了裝箱操作。

說(shuō)明:盡量在使用時(shí)直接聲明非空基本類型的區(qū)間,不要間接調(diào)用,來(lái)避免額外區(qū)間類的創(chuàng)建?;蛘咧苯勇暶鳛槌A縼?lái)復(fù)用。

區(qū)間也可以用于其他實(shí)現(xiàn)了 Comparable 的非基本類型。

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

在這種情況下,最終實(shí)現(xiàn)并不會(huì)優(yōu)化,而且總是會(huì)創(chuàng)建一個(gè) ClosedRange 對(duì)象,如下面編譯后的代碼所示:

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

迭代:for 循環(huán)

整型區(qū)間 (除了 Float 和 Double之外其他的基本類型)也是 級(jí)數(shù):它們可以被迭代。這就可以將經(jīng)典 Java 的 for 循環(huán)用一個(gè)更短的表達(dá)式替代。

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

經(jīng)過(guò)編譯器優(yōu)化后的代碼實(shí)現(xiàn)了零額外開(kāi)銷:

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

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

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

編譯之后,這也實(shí)現(xiàn)了零額外開(kāi)銷:

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

然而,其他迭代器參數(shù)并沒(méi)有如此好的優(yōu)化。反向迭代還有一種結(jié)果相同的方式,使用 reversed() 方法結(jié)合區(qū)間:

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

編譯后的代碼并沒(méi)有看起來(lá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;
}

會(huì)創(chuàng)建一個(gè)臨時(shí)的 IntRange 對(duì)象來(lái)代表區(qū)間,然后創(chuàng)建另一個(gè) IntProgression 對(duì)象來(lái)反轉(zhuǎn)前者的值。

事實(shí)上,任何結(jié)合不止一個(gè)方法來(lái)創(chuàng)建遞進(jìn)都會(huì)生成類似的至少創(chuàng)建兩個(gè)微小遞進(jìn)對(duì)象的代碼。

這個(gè)規(guī)則也適用于使用 step() 中綴方法來(lái)操作遞進(jìn)的步驟,即使只有一步:

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

一個(gè)次要提示,當(dāng)生成的代碼讀取 IntProgression 的 last 屬性時(shí)會(huì)通過(guò)對(duì)邊界和步長(zhǎng)的小小計(jì)算來(lái)決定準(zhǔn)確的最后值。在上面的代碼中,最終值是 9。

最后,until() 中綴函數(shù)對(duì)于迭代也很有用,該函數(shù)(執(zhí)行結(jié)果)不包含最大值。

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

遺憾的是,編譯器并沒(méi)有針對(duì)這個(gè)經(jīng)典的包含區(qū)間圍優(yōu)化,迭代器依然會(huì)創(chuàng)建區(qū)間對(duì)象:

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 可以提升的另一個(gè)點(diǎn),與此同時(shí),可以通過(guò)這樣寫來(lái)優(yōu)化代碼:

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

說(shuō)明:

for 循環(huán)內(nèi)部的迭代,最好只用區(qū)間表達(dá)式的一個(gè)單獨(dú)方法來(lái)調(diào)用 .. 或 downTo() 來(lái)避免額外臨時(shí)遞進(jìn)對(duì)象的創(chuàng)建。

迭代:forEach()

作為 for 循環(huán)的替代,使用區(qū)間內(nèi)聯(lián)的擴(kuò)展方法 forEach() 來(lái)實(shí)現(xiàn)相似的效果可能更吸引人。

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

但如果仔細(xì)觀察這里使用的 forEach() 方法簽名的話,你就會(huì)注意到并沒(méi)有優(yōu)化區(qū)間,而只是優(yōu)化了 Iterable,所以需要?jiǎng)?chuàng)建一個(gè) 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);
}

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

迭代:集合

Kotlin 標(biāo)準(zhǔn)庫(kù)提供了內(nèi)置的 indices 擴(kuò)展屬性來(lái)生成數(shù)組和 Collection 的區(qū)間。

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

令人驚訝的是,對(duì)這個(gè) indices 的迭代得到了編譯器的優(yōu)化:

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;
 }
}

從上面的代碼中我們可以看到?jīng)]有創(chuàng)建 IntRange 對(duì)象,列表的迭代是以最高效率的方式運(yùn)行的。

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

inline val SparseArray<*>.indices: IntRange
 get() = 0..size() - 1

fun printValues(map: SparseArray<String>) {
 for (i in map.indices) {
 println(map.valueAt(i))
 }
}

但編譯之后,我們可以發(fā)現(xiàn)這并沒(méi)有那么高效率,因?yàn)榫幾g器無(wú)法足夠智能地避免區(qū)間對(duì)象的產(chǎn)生:

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;
 }
 }
}

所以,我會(huì)建議你避免聲明自定義的 lastIndex 擴(kuò)展屬性:

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

說(shuō)明:當(dāng)?shù)鷽](méi)有聲明 Collection 的自定義集合 時(shí),直接在 for 循環(huán)中寫自己的序列區(qū)間而不是依賴方法或?qū)傩詠?lái)生成區(qū)間,從而避免區(qū)間對(duì)象的創(chuàng)建。

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

最新評(píng)論