Make writing a habit together! This is the third day of my participation in the “Gold Digging Day New Plan · April More text Challenge”. Click here for more details.

1. Three ways of singleton implementation

  • object

First look at the code:

object Pro {
}
Copy the code

So simple implementation of Pro singleton, we can decompile into Java code to see the specific implementation principle:

public final class Pro {
   @NotNull
   public static final Pro INSTANCE;

   private Pro(a) {}static {
      Pro var0 = newPro(); INSTANCE = var0; }}Copy the code

First, the constructor of the Pro class is declared private. Second, we can see that this is implemented through the static code block singleton, using the class static code block will only be executed once, which is thread-safe and hunger-style.

Whereas Java gets this singleton through pro.instance, Kotlin gets it directly through Pro

  • lazy

Code first:

class Pro private constructor() {
    companion object {
        val INSTANCE by lazy {
            Pro()
        }
    }
}
Copy the code

The default implementation of lazy is thread-safe. This is a thread-safe and lazy singleton implementation. To learn more about lazy, refer to one of Kotlin’s development practices

  • Double check lock

The code:

@Volatile
var singleton: Pro? = null

fun getInstance(a): Pro {
    if (singleton == null) {
        synchronized(Pro::class.java) {
            if (singleton == null) {
                singleton = Pro()
            }
        }
    }
    return singleton!!
}
Copy the code

Here is the Kotlin implementation of the Java double-checked lock:

  1. @VolatileEnsure code instruction order
  2. getInstanceMethod inner and outer layersingletonSentenced to empty guaranteesingletonNow that the initialization is complete, the thread should not compete for the lock
  3. getInstanceMethod inner inner layersingletonNullify is guaranteed if the thread has been initialized beforesingletonThat’s done. Subsequent threads should not repeat initialization

As you can see, this is a singleton implemented in a thread-safe and lazy manner

2.typealiasGive complex types individual names

The typeAlias keyword is mainly used to give individual names to types. The following two scenarios are used:

  • The function type is alias

Function types should be common in everyday development, such as

// Concatenates Int and String and returns String
val block: ((Int, String) -> String)? = null
Copy the code

This function type (Int, String) -> String) is cumbersome to write and unreadable, and it is time to upload the TypeAlias:

typealias Concat = (Int, String) -> String
val block: Concat? = null
Copy the code

Alias (Int, String) -> String) to Concat is not only convenient to use, but also easy to see where this function type is used: concatenation

  • Simplify generic passing

When using a ViewModel, we might often wrap the return from an interface as follows:

class MainViewModel: ViewModel() {
    val data: MutableLiveData<Response<String>> = MutableLiveData()
    
    fun test(a) {
        data.value = Response("haha")}data class Response<T>(val data: T? = null)}Copy the code

Response is used to encapsulate the server return, and the generic T represents the entity class into which the Response data can be deserialized.

As you can see above, each MutableLiveData definition must declare Response

in its generic type. Since Response is a unified encapsulation of all interface responses, it is a certain type. T in Response

is the type that needs to be specified dynamically each time MutableLiveData is created.

Response

: Response

: Response


typealias ExternalLiveData<T> = MutableLiveData<MainViewModel.Response<T>>
Copy the code

Each time you create MutableLiveData, you can write:

val data1: ExternalLiveData<String> = MutableLiveData()
Copy the code