The keyword

  • synchorinzed
  • CAS
  • Delegate/proxy mode

entrust

To understand the role and use of Kotlin-delegates, you need to understand what a delegate is. At first glance, if you do not understand the words of the commission, you may as well convert into the agent. Delegate pattern and proxy pattern are just two names for a design pattern.

Delegate/proxy mode

The agency mode, literally understood, means that a third party is required to do the things that are inconvenient or cannot be done by oneself, and finally achieve the desired purpose or effect through the third party. Example: Employee Xiao Li is working in B’s head office, and XIAO Li doesn’t give overtime pay to work overtime days. Xiao Li can’t stand it anymore, so he wants to go to court to Sue Manager B. Although the law allows you to Sue without a lawyer, you can represent yourself. But xiao Li first is not familiar with the specific process of legal prosecution, the second mouth is more stupid, a person’s legs will shake badly. So Xiao Li decided to go to a lawyer for help. There are some similarities and differences between going to court with a lawyer and going to court yourself.

The similarities are as follows:

  • The plaintiff’s name, age, reason and purpose are all required.
  • All need to go through the forensic investigation of the court, court debates and other processes.
  • Finally get the verdict.

The differences are:

  • Xiao Li save trouble, let professional people do professional things, do not need to understand their own court that a set of complicated process.
  • More assured.

From the example above, we noticed that the proxy pattern has several key points.

  • Proxy role (Xiao Li)
  • Acting Role (lawyer)
  • Agreement (an agreement that is abstracted from something that needs to be done by both the agent and the agent)

UMLClass diagram:

The code implementation is as follows:

Public void register(String name); Public void dosomething(); Public void notifys(); } class LawyerProxy implements Protocol{private Employer Employer; public LawyerProxy(Employer employer){ this.employer=employer; } @Override public void register(String name) { // TODO Auto-generated method stub this.employer.register(name); } public void collectInfo(){system.out.println (" As a lawyer, I need to collate and investigate the information provided by the employer, and provide written evidence to the court." ); } @Override public void dosomething() { // TODO Auto-generated method stub collectInfo(); this.employer.dosomething(); finish(); {} public void finish () System. The out. Println (" the lawsuit played the..." ); } @Override public void notifys() { // TODO Auto-generated method stub this.employer.notifys(); }} // Implements Protocol{String name=null; @Override public void register(String name) { // TODO Auto-generated method stub this.name=name; } @override public void dosomething() {// TODO auto-generated method stub system.out.println (" "+this.name+" ") He makes me work overtime every day without pay." ); } @override public void notifys() {// TODO auto-generated method stub system.out.println (" the court ruled that the case was won, and B needed to pay 100000 yuan for mental compensation." ); } } public class Client { public static void main(String[] args) { Employer employer=new Employer(); System.out.println(" I can't take it anymore, I'm going to Sue my boss "); System.out.println(" find a lawyer......" ); Protocol lawyerProxy=new LawyerProxy(employer); Lawyerproxy. register(" Blossoming "); lawyerProxy.dosomething(); lawyerProxy.notifys(); }}Copy the code

After running, print the following:

I can't stand, I want to Sue boss to look for lawyer to solve...... As a lawyer, I need to sort out and investigate the information provided by my employer, write written documents for the court, and provide evidence. I want to Sue President B for making me work overtime without pay every day. This lawsuit finished............... The court ruled that the case was won and B needed to pay 100,000 yuan in emotional compensation.Copy the code

Commissioned by class

With some understanding of the proxy pattern, let’s look at how the Kotlin – class delegate is implemented:

interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

class Derived(b: Base) : Base by b

fun main() {
    val b = BaseImpl(10)
    Derived(b).print()
}
Copy the code

This is an example of the Kotlin Language Chinese site, converted to Javaa code as follows:

public interface Base { void print(); } // BaseImpl.java public final class BaseImpl implements Base { private final int x; public void print() { int var1 = this.x; boolean var2 = false; System.out.print(var1); } public final int getX() { return this.x; } public BaseImpl(int x) { this.x = x; } } // Derived.java public final class Derived implements Base { // $FF: synthetic field private final Base $$delegate_0; public Derived(@NotNull Base b) { Intrinsics.checkNotNullParameter(b, "b"); super(); this.$$delegate_0 = b; } public void print() { this.$$delegate_0.print(); } } // DelegateTestKt.java public final class DelegateTestKt { public static final void main() { BaseImpl b = new BaseImpl(10); (new Derived((Base)b)).print(); } // $FF: synthetic method public static void main(String[] var0) { main(); }}Copy the code

You can see that the abstract method of the Base interface is already implemented in Derived, and that the actual caller to the method is the Base instance object passed in when the object was constructed, the BaseImpl instance object.

Compare the agency model described above:

  • Base: Agency Agreement
  • BaseImpl: Proxy role
  • Derived: represented By The represented role

In this case, it is clear why the results of the class delegate example above include the results generated by the override method implementation and member variables.

Attribute to entrust

The attribute delegates provided in the Kotlin library are:

  • lazy: Delay attribute;
  • Delegates.notNull(): cannot be empty;
  • Delegates.observable(): Observable attributes;
  • Delegates.vetoable(): Observable attribute, can refuse to modify the attribute;

The lazy delay property will be analyzed in the following ways: Delegates.

The provided standard attribute Delegate methods are defined in the delegate. kt file, and the code is too small to post. You can see that all three delegate methods return an instance object of the ReadWriteProperty interface, with the ReadOnlyProperty interface at the top. The names are indicative of their respective functions:

  • ReadOnlyProperty: only for readable attributes,val;
  • ReadWriteProperty: for read-write properties,var.

In the implementation of the attribute delegate, the role of the corresponding proxy pattern is as follows:

  • Protocol:ReadOnlyPropertyReadWriteProperty;
  • Agents:Delegate;
  • Proxied: Actually uses properties.

Delegates.notnull () is easier, if we analyze how the attribute delegate is implemented.

private class NotNullVar<T : Any>() : ReadWriteProperty<Any? , T> { private var value: T? = null public override fun getValue(thisRef: Any? , property: KProperty<*>): T { return value ? : throw IllegalStateException("Property ${property.name} should be initialized before get.") } public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { this.value = value } }Copy the code
class DelegateTest {
    private val name: String by Delegates.notNull()
}
Copy the code

Turn kotlin Java

public final class DelegateTest {
   // $FF: synthetic field
   static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(DelegateTest.class, "name", "getName()Ljava/lang/String;", 0))};
   private final ReadWriteProperty name$delegate;

   private final String getName() {
      return (String)this.name$delegate.getValue(this, $$delegatedProperties[0]);
   }

   public DelegateTest() {
      this.name$delegate = Delegates.INSTANCE.notNull();
   }
}
Copy the code

You can see that the name attribute delegates to the value attribute of NotNullVar. When we access the name property, we are actually accessing the value property of NotNullVar.

Custom delegate

Delegates mentioned above all delegate methods in Delegates return an instance object of the ReadWriteProperty interface. If you need to customize a delegate, you can do so by implementing the ReadWriteProperty interface.

  • varProperty custom delegate: inheritanceReadWritePropertyInterface, and implementGetValue () and setValue ()Methods;
  • valProperty custom delegate: implementationReadOnlyPropertyInterface, and implementgetValueMethods.
public override operator fun getValue(thisRef: T, property: KProperty<*>): V

public operator fun setValue(thisRef: T, property: KProperty<*>, value: V)
Copy the code

The parameters are as follows:

  • thisRefIt must be the same type as the owner of the property or a supertype of the property.
  • property– must beKProperty<*>Type or its supertype.

Lazy

Lazy is analyzed here because although it also delegates properties to properties of other classes, it does not inherit ReadWriteProperty or the ReadOnlyProperty interface is not a standard attribute delegate.

Lazy:

public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

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

The lazy function takes two arguments:

  • LazyThreadSafetyMode: thread-safe mode;
  • initializer: initializes the function.

LazyThreadSafetyMode: Different modes are used as follows:

  • SYNCHRONIZEDThrough:Volatile + synchorinzedThe lock ensures that the initialization function is called only once and the variable is assigned only once in the case of multiple threads.
  • PUBLICATIONThrough:Volatile + CASEnsures that the variable is assigned only once in the case of multiple threads;
  • NONE: The thread is unsafe.

The lazy function returns an instance of the lazy interface.

Note: You should not use NONE unless you can guarantee that lazy instances will never be initialized in multiple threads.

The lazy function returns different instance objects based on the selected schema: SynchronizedLazyImpl, SafePublicationLazyImpl, and UnsafeLazyImpl. The biggest difference between the three is the implementation of the getter() function, but ultimately the value property in each class is the property that the lazy function modifies.

Synchorinzed, CAS, and synchorinzed are common problems in multi-threaded locking. For more information about them, see my previous article:

  • See through Synchronized from EventBus
  • CAS can actually replace Synchorinzed

application

The Kotlin delegate can be applied in a project to help with the following shorthand:

  • Fragment / ActivityThe ginseng
  • ViewBinding

The two examples written in this section are taken from

Kotlin | entrusted mechanism & principle & application – peng ugly View Binding and Kotlin entrusted property of clever union, junk code! By Kirill Rozov, translated by Van Terse

Kotlin delegate + Fragment/Activity pass parameters

Sample source: peng ugly – Kotlin | entrusted mechanism & principle & application project address: making – DemoHall

Before attribute delegate:

class OrderDetailFragment : Fragment(R.layout.fragment_order_detail) { private var orderId: Int? = null private var orderType: Int? = null companion object { const val EXTRA_ORDER_ID = "orderId" const val EXTRA_ORDER_TYPE = "orderType"; fun newInstance(orderId: Int, orderType: Int?) = OrderDetailFragment().apply { Bundle().apply { putInt(EXTRA_ORDER_ID, orderId) if (null ! = orderType) { putInt(EXTRA_ORDER_TYPE, orderType) } }.also { arguments = it } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) arguments? .let { orderId = it.getInt(EXTRA_ORDER_ID, 10000) orderType = it.getInt(EXTRA_ORDER_TYPE, 2) } } }Copy the code

Define ArgumentDelegate. Kt

fun <T> fragmentArgument() = FragmentArgumentProperty<T>() class FragmentArgumentProperty<T> : ReadWriteProperty<Fragment, T> { override fun getValue(thisRef: Fragment, property: KProperty<*>): T { return thisRef.arguments? .getValue(property.name) as? T ?: throw IllegalStateException("Property ${property.name} could not be read") } override fun setValue(thisRef: Fragment, property: KProperty<*>, value: T) { val arguments = thisRef.arguments ?: Bundle().also { thisRef.arguments = it } if (arguments.containsKey(property.name)) { // The Value is not expected to be modified return } arguments[property.name] = value } }Copy the code

After using attribute delegate:

class OrderDetailFragment : Fragment(R.layout.fragment_order_detail) {

    private lateinit var tvDisplay: TextView

    private var orderId: Int by fragmentArgument()
    private var orderType: Int? by fragmentArgumentNullable(2)

    companion object {
        fun newInstance(orderId: Int, orderType: Int?) = OrderDetailFragment().apply {
            this.orderId = orderId
            this.orderType = orderType
        }
    }

    override fun onViewCreated(root: View, savedInstanceState: Bundle?) {
        // Try to modify (UnExcepted)
        this.orderType = 3
        // Display Value
        tvDisplay = root.findViewById(R.id.tv_display)
        tvDisplay.text = "orderId = $orderId, orderType = $orderType"
    }
}
Copy the code

Kotlin delegate + ViewBinding

The sample source: ViewBindingPropertyDelegate

Before attribute delegate:

class ProfileActivity : AppCompatActivity(R.layout.activity_profile) { private var binding: ActivityProfileBinding? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityProfileBinding.inflate(layoutInflater) binding!! .profileFragmentContainer } }Copy the code

After attribute delegate:

class ProfileActivity : AppCompatActivity(R.layout.activity_profile) {

    private val viewBinding: ActivityProfileBinding by viewBinding()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        with(viewBinding) {
            profileFragmentContainer
        }
    }
}
Copy the code

After using the code is very simple and do not need to use again!! Or define a new variable, interested students can look at the source code.