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:
@Volatile
Ensure code instruction ordergetInstance
Method inner and outer layersingleton
Sentenced to empty guaranteesingleton
Now that the initialization is complete, the thread should not compete for the lockgetInstance
Method inner inner layersingleton
Nullify is guaranteed if the thread has been initialized beforesingleton
That’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.typealias
Give 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