真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Kotlin中委托屬性與區(qū)間的示例分析-創(chuàng)新互聯(lián)

這篇文章主要介紹了Kotlin中委托屬性與區(qū)間的示例分析,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

創(chuàng)新互聯(lián)總部坐落于成都市區(qū),致力網(wǎng)站建設服務有成都網(wǎng)站設計、網(wǎng)站建設、網(wǎng)絡營銷策劃、網(wǎng)頁設計、網(wǎng)站維護、公眾號搭建、小程序制作、軟件開發(fā)等為企業(yè)提供一整套的信息化建設解決方案。創(chuàng)造真正意義上的網(wǎng)站建設,為互聯(lián)網(wǎng)品牌在互動行銷領域創(chuàng)造價值而不懈努力!

委托屬性

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

  • 延遲屬性(lazy properties): 其值只在首次訪問時計算,

  • 可觀察屬性(observable properties): 監(jiān)聽器會收到有關此屬性變更的通知,

  • 把多個屬性儲存在一個映射(map)中,而不是每個存在單獨的字段中。

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

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

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

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

委托對象必須實現(xiàn)一個擁有 getValue() 方法的操作符,以及 setValue() 方法來實現(xiàn)讀/寫屬性。些方法將會接受包含對象實例以及屬性元數(shù)據(jù)作為額外參數(shù)。當一個類聲明委托屬性時,編譯器生成的代碼會和如下 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, "");
 this.p$delegate.setValue(this, $$delegatedProperties[0], var1);
 }
}

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

委托實例

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

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ù)時,需要建立新的委托實例,并將其傳遞到構造器中。

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

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

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

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

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

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

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

泛型委托

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

private var maxDelay: Long by SharedPreferencesDelegate()

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

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

標準委托:lazy()

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

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

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

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

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

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

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

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

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

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

區(qū)間

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

包含

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

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

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

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

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

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

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

比如下面的代碼:

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

在編譯后會創(chuàng)建一個額外的 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 的方法也無法避免這個對象的創(chuàng)建。這是 Kotlin 1.1 編譯器可以優(yōu)化的一個點。至少通過這些特定的區(qū)間類避免了裝箱操作。

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

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

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

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

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

迭代:for 循環(huán)

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

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

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

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

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

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

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

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

會創(chuàng)建一個臨時的 IntRange 對象來代表區(qū)間,然后創(chuàng)建另一個 IntProgression 對象來反轉前者的值。

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

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

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

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

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

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

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

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 可以提升的另一個點,與此同時,可以通過這樣寫來優(yōu)化代碼:

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

說明:

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

迭代:forEach()

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

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

但如果仔細觀察這里使用的 forEach() 方法簽名的話,你就會注意到并沒有優(yōu)化區(qū)間,而只是優(yōu)化了 Iterable,所以需要創(chuàng)建一個 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)建一個 IntRange 對象,還需要額外創(chuàng)建 IntIterator。但至少它還是生成了基本類型的值。迭代區(qū)間時,最好只使用 for 循環(huán)而不是區(qū)間上的 forEach() 方法來避免額外創(chuàng)建一個迭代器。

迭代:集合

Kotlin 標準庫提供了內(nèi)置的 indices 擴展屬性來生成數(shù)組和 Collection 的區(qū)間。

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

令人驚訝的是,對這個 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 對象,列表的迭代是以最高效率的方式運行的。

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

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

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

但編譯之后,我們可以發(fā)現(xiàn)這并沒有那么高效率,因為編譯器無法足夠智能地避免區(qū)間對象的產(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;
 }
 }
}

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

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

說明:當?shù)鷽]有聲明 Collection 的自定義集合 時,直接在 for 循環(huán)中寫自己的序列區(qū)間而不是依賴方法或?qū)傩詠砩蓞^(qū)間,從而避免區(qū)間對象的創(chuàng)建。

感謝你能夠認真閱讀完這篇文章,希望小編分享的“Kotlin中委托屬性與區(qū)間的示例分析”這篇文章對大家有幫助,同時也希望大家多多支持創(chuàng)新互聯(lián),關注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關知識等著你來學習!


分享名稱:Kotlin中委托屬性與區(qū)間的示例分析-創(chuàng)新互聯(lián)
文章路徑:http://weahome.cn/article/ipees.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部