Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Example Analysis of delegation attribute and interval in Kotlin

2025-04-01 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly introduces the Kotlin delegated attributes and interval example analysis, has a certain reference value, interested friends can refer to, I hope you can learn a lot after reading this article, the following let the editor with you to understand.

Delegate attribute

There are some common attribute types, and although we can implement them manually every time we need them, it would be better if we could implement them only once and put them into a library for everyone. For example, include

Delay property (lazy properties): its value is calculated only on first access

Observable property (observable properties): listeners will be notified of changes to this property

Multiple attributes are stored in a single map instead of each in a separate field.

To cover these (and other) situations, Kotlin supports delegate attributes:

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

A delegate attribute is a property that has getter and optional setter through a delegate implementation, and allows reusable custom properties to be implemented. For example:

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

The delegate object must implement an operator that has the getValue () method, as well as the setValue () method to implement the read / write properties. These methods will accept including object instances and property metadata as additional parameters. When a class declares a delegate property, the compiler generates code similar to the following Java code.

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

Some static attribute metadata is added to the class, and the delegate is initialized in the constructor of the class and called each time the property is read or written.

Delegate instance

In the above example, a new delegate instance is created to implement the property. This requires the implementation of the delegate to be stateful, for example, when the calculation results are cached internally:

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

At the same time, when additional parameters are needed, a new delegate instance needs to be created and passed to the constructor.

Class Example {privateval nameView by BindViewDelegate (R.id.name)}

But there are cases where only one delegate instance is needed to implement any property: when the delegate is stateless and the only variable it needs is the already provided containing object instance and delegate name, you can implement a singleton delegate by declaring it as object instead of class.

For example, the following singleton delegate retrieves the Fragment that matches the given tag from the Android Activity.

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

Similarly, any existing class can be extended to become a delegate. GetValue () and setValue () can also be declared as extension methods to implement. Kotlin has provided built-in extension methods to allow the Map and MutableMap instance to be used as a delegate and the attribute name as the key in it.

If you choose to reuse the same local delegate instance to implement multiple attributes in a class, you need to initialize the instance in the constructor.

Note: starting with Kotlin 1.1, you can also declare method local variables to be declared as delegate properties. In this case, the delegate can not be initialized until the variable is declared inside the method, without having to perform initialization in the constructor.

Generic delegation

Delegate methods can also be declared generic so that different types of properties can reuse the same delegate class.

Private var maxDelay: Long by SharedPreferencesDelegate ()

However, if a generic delegate is used for a base type as in the previous example, boxing and unboxing will be triggered each time the property is read and written, even if the declared base type is not empty.

Note: for delegate properties of a non-empty base type, it is best to use a specific delegate class of a given type rather than a generic delegate to avoid the extra overhead of boxing each time the property is accessed.

Standard delegation: lazy ()

For common situations, Kotlin provides standard delegates such as Delegates.notNull (), Delegates.observable (), and lazy ().

Lazy () is a delegate that calculates the initial value of a property with a given lambda value on the first read and returns a read-only property.

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

This is a concise way to delay costly initialization to when it is really needed, improving performance while preserving the readability of the code.

Note that lazy () is not an inline function, and the lambda parameter passed in will be compiled into an additional Function class and will not be inlined into the returned delegate object.

What is often overlooked is that lazy () has an optional mode parameter to determine which of the three delegates should be returned:

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

The default mode LazyThreadSafetyMode.SYNCHRONIZED will provide a relatively expensive double-checking lock to ensure that the initialization block can be safely executed once the attribute can be read from multiple threads.

If you are sure that the property will only be accessed in a single thread (such as the main thread), you can choose LazyThreadSafetyMode.NONE instead to avoid the extra overhead of using locks.

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

Interval

Interval is a special expression used in Kotlin to represent a finite set of values. The value can be of any Comparable type. These expressions take the form of creating methods that declare the ClosedRange interface. The main way to create an interval is.. Operator method.

Include

The main purpose of interval expressions is to use the in and! in operators to include and exclude.

If (I in 1.. 10) {println (I)}

The implementation is optimized for intervals of non-empty primitive types (including values for Int, Long, Byte, Short, Float, Double, and Char), so the above code can be optimized like this:

If (1 "Find it somewhere else" else-> "Oops"}

Compared to a series of if {... } else if {... } code block, which improves the readability of the code without reducing efficiency. However, range has some small additional overhead if there is at least one indirect call between declaration and use.

For example, the following code:

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

An additional IntRange object is created after compilation:

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

The method of declaring the getter of the property as inline cannot avoid the creation of this object. This is a point where the Kotlin 1.1 compiler can be optimized. Boxing operations are avoided at least through these specific interval classes.

Description: try to declare the interval of the non-empty basic type directly when using it, and do not call it indirectly to avoid the creation of additional interval classes. Or it can be directly declared as a constant for reuse.

Intervals can also be used for other non-basic types that implement Comparable.

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

In this case, the final implementation is not optimized, and a ClosedRange object is always created, as shown in the following compiled code:

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

Iterations: for loop

Integer intervals (basic types other than Float and Double) are also series: they can be iterated. This replaces the classic Java for loop with a shorter expression.

For (I in 1.. 10) {println (I)}

The compiler-optimized code achieves zero extra overhead:

Int I = 1 break; byte var3 = 10 if (I = var3) {while (true) {System.out.println (I); if (I = = var3) {break;}-- I;}}

However, other iterator parameters are not so well optimized. Reverse iteration also has the same result, using the reversed () method to combine the interval:

For (I in (1.. 10). Reversed ()) {println (I)}

The compiled code is not as small as it seems:

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

A temporary IntRange object is created to represent the interval, and then another IntProgression object is created to reverse the former value.

In fact, any combination of more than one method to create progression will generate similar code to create at least two tiny progressive objects.

This rule also applies to using the step () suffix method to manipulate progressive steps, even if there is only one step:

For (I in 1.. 10 step 2) {println (I)}

A secondary hint is that when the generated code reads the last property of IntProgression, the exact final value is determined by a small calculation of the boundary and step size. In the above code, the final value is 9.

Finally, the until () suffix function is also useful for iterations, and the function (the result of execution) does not contain a maximum value.

For (I in 0 until size) {println (I)}

Unfortunately, the compiler does not optimize for this classic inclusion interval, and the iterator still creates interval objects:

IntRange var10000 = RangesKt.until (0, size); int I = var10000.getFirst (); int var1 = var10000.getLast (); if (I)

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report