• Kotlin: We are different

  • Kotlin: New gameplay you haven’t played yet

  • Article 3: Kotlin for all: Coroutines Special

This article has been authorized to hongyang wechat public account reprint

directory

  • Air safety

  • Method supports adding default parameters

  • The parameters above the method are immutable

  • Class method extension

  • The function variables

  • Inline function

  • Trust mechanism

    • Commissioned by class

    • Attribute to entrust

    • Lazy to entrust

  • Extension function

    • Let the function

    • With the function

    • The run function

    • The apply function

    • The braking function

  • Operator overloading

Air safety

  • In Java, we don’t have to deal with null objects, so NullPointerException is often the result. Now Kotlin has qualified the null object to handle NullPointerException at compile time, otherwise the compile will fail

  • In cases where the object is not null, you can use the object directly

fun getText(a) : String {
    return "text"
}
Copy the code
val text = getText()
print(text.length)
Copy the code
  • In the case of nullable objects, you must determine whether the object is null
fun getText(a) : String? {
    return null
}
Copy the code
val text = getText()
if(text ! =null) {
    print(text.length)
}
Copy the code
If the text object is null, the null pointer exception will be reported. This is not recommended in general
valtext = getText() print(text!! .length)Copy the code
// If the text object is empty, no error is reported, but the text.length result is null
valtext = getText() print(text? .length)Copy the code

Method supports adding default parameters

  • On Java methods, we might reload a method multiple times to extend it
public void toast(String text) {
    toast(this, text, Toast.LENGTH_SHORT);
}

public void toast(Context context, String text) {
    toast(context, text, Toast.LENGTH_SHORT);
}

public void toast(Context context, String text, int time) {
    Toast.makeText(context, text, time).show();
}
Copy the code
toast("Pop a toast.");
toast(this."Pop a toast.");
toast(this."Pop a toast.", Toast.LENGTH_LONG);
Copy the code
  • With Kotlin, however, we don’t need to overload and can define the default values of parameters directly on the method
fun toast(context : Context = this, text : String, time : Int = Toast.LENGTH_SHORT) {
    Toast.makeText(context, text, time).show()
}
Copy the code
toast(text = "Pop a toast.")
toast(this."Pop a toast.")
toast(this."Pop a toast.", Toast.LENGTH_LONG)
Copy the code

The parameters above the method are immutable

  • In The case of A Java method, we can change the parameter assignment of the method, but in the case of Kotlin, we cannot change the parameter assignment of the Kotlin method. The variable is val (corresponding to Java final), so we have two solutions:

  • First, define an identical variable in the method as follows:

class XxxView : View {

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        var widthMeasureSpec: Int = widthMeasureSpec
        var heightMeasureSpec: Int = heightMeasureSpec
        
        if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) {
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(30, MeasureSpec.EXACTLY)
        }

        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(30, MeasureSpec.EXACTLY)
        }
        
        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec)
    }
}
Copy the code
  • However, the compiler will warn us that a duplicate variable is present, but it will compile and run normally, so it is not recommended

  • Second, define a variable with a different name in the method as follows:

class XxxView : View { override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { var finalWidthMeasureSpec: Int = widthMeasureSpec var finalHeightMeasureSpec: Int = heightMeasureSpec if (MeasureSpec.getMode(finalWidthMeasureSpec) == MeasureSpec.AT_MOST) { finalWidthMeasureSpec =  MeasureSpec.makeMeasureSpec(30, MeasureSpec.EXACTLY) } if (MeasureSpec.getMode(finalHeightMeasureSpec) == MeasureSpec.AT_MOST) { finalHeightMeasureSpec = MeasureSpec.makeMeasureSpec(30, MeasureSpec.EXACTLY) } setMeasuredDimension(widthMeasureSpec, heightMeasureSpec) } }Copy the code
  • Adding a final prefix to the original one not only eliminates the compiler warning, but also eliminates the need to come up with a new name for the variable.

  • Is there a way to change it like Java? I struggled with this problem for a while, but after consulting many documents and materials, I finally found that there was no way out, so I had to compromise. After all, nothing in the world is perfect.

Class method extension

  • You can extend methods of an existing class without inheritance, such as the String class
fun String.handle(a) : String {
    return this + Android Wheels
}
Copy the code
// It is important to note in which class the Handle method is defined. This extension can only be used in that class
print("HJQ = ".handle())
Copy the code
HJQ = Android Wheel guyCopy the code

The function variables

  • Functions can be passed as variables in Kotlin syntax
var result = fun(number1 : Int, number2 : Int) : Int {
    return number1 + number2
}
Copy the code
  • Use this function variable
println(result(1.2))
Copy the code

Inline function

  • One might ask, what is an inline function? For example, I use Kotlin to write the following code
class Demo {

    fun test(a) {
        showToast("666666")}/** * This is our hero today: inline functions, using the keyword */ inline
    private inline fun showToast(message: String) {
        ToastUtils.show(message)
    }
}
Copy the code
  • After decompiling, the following code is generated:
/* compiled from: Demo.kt */
public final class Demo {

    public final void test(a) {
        ToastUtils.show("666666"); }}Copy the code
  • An inline function replaces the code that calls an inline function with the code in the inline function at compile time. It actually improves the performance of the code, just as constants of basic data types are optimized during compilation, but it also increases the code if inline functions are called in many places and the implementation of inline functions has a lot of code.

  • In addition, in the above code example, the compiler has a code warning over the inline keyword that says:

Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types

The expected impact of inlining on performance is negligible. Inlining works best with functions whose arguments are of function type

  • In the above code example, the performance benefit of the inline function is negligible and is more suitable for functions with lambda parameters. With this tip, modify the above code example to the following so that no code warning is generated:
class Demo {

    fun test(a) {
    
        showToast({
            println("Test output.")},"7777777")}private inline fun showToast(function: () -> Unit, message: String) {
        function.invoke()
        ToastUtils.show(message)
    }
}
Copy the code
  • So, one might wonder, what’s the big performance boost? What is the basis for judging? Let’s do a set of experiments. What’s the difference between decompressed code with inline and no inline? Let’s look at what the decompressed code looks like with inline
/* compiled from: Demo.kt */
public final class Demo {

    public final void test() {
        System.out.println("\u6d4b\u8bd5\u8f93\u51fa\u4e86");
        ToastUtils.show("7777777"); }}Copy the code
  • That’s as expected, but what happens when you decomcompile without inline?
/* compiled from: Demo.kt */
public final class Demo {

    public final void test() {
        showToast(1.INSTANCE, "7777777");
    }

    private final void showToast(Function0<Unit> function, String message) { function.invoke(); ToastUtils.show(message); }}Copy the code
/* compiled from: Demo.kt */
final class Demo$testThe $1extends Lambda implements Function0<Unit> {
    public static final Demo$test$1 INSTANCE = new Demo$test$1(a); Demo$test$1() {
        super(0);
    }

    public final void invoke() {
        System.out.println("\u6d4b\u8bd5\u8f93\u51fa\u4e86"); }}Copy the code
  • Obviously, not inline will result in the generation of one more inner class, which is a continuation of the lambda function, and the example inside will be static, which will definitely increase memory consumption, as well as the additional benefit of reducing the stack of method calls.

  • In addition to the inline keyword, there is another keyword, noinline, which may confuse you. If I don’t write inline above the method, it will not be inline. So what does this keyword do? If a function has multiple lambda arguments inline, I only want to inline one of the lambda arguments. If the other lambda arguments are not inline, Lambda arguments that do not need to be inline can be decorated with this keyword as follows:

private inline fun showToast(function1: () -> Unit.noinline function2: () -> Unit, message: String) {
    function1.invoke()
    function2.invoke()
    ToastUtils.show(message)
}
Copy the code

Trust mechanism

Commissioned by class
  • Let’s look at a piece of code first
// Define the log policy interface
interface ILogStrategy {

    fun log(message: String)
}
Copy the code
// Implement a default logging policy class
class LogStrategyImpl : ILogStrategy {

    override fun log(message: String) {
        Log.i("Test output", message)
    }
}
Copy the code
// Create a logging proxy class
class LogStrategyProxy(strategy: ILogStrategy) : ILogStrategy by strategy
Copy the code
  • You might be a little confused by this

    • ILogStrategy by strategy

    • Wouldn’t the LogStrategyProxy class not implement interface methods cause compilation failures?

  • I think the same explanation can be used for both problems. LogStrategyProxy does not implement the log method of ILogStrategy because it adds by Strategy to the end of the ILogStrategy interface, The strategy object is the variable in the LogStrategyProxy constructor, which means that the implementation of this interface is implemented by the Strategy object. I don’t need to implement it again. Isn’t this similar to static proxies in Java? Except for the Kotlin class delegate feature where the compiler automatically generates interface method code, you can imagine it like this:

class LogStrategyProxy(val strategy: ILogStrategy) : ILogStrategy {

    override fun log(message: String) {
        strategy.log(message)
    }
}
Copy the code
  • One must ask: why should I believe you are such a code?

  • This is a good question, I provide the decompiled code, you can see it:

public final class LogStrategyProxy implements ILogStrategy {

    private final /* synthetic */ ILogStrategy $$delegate_0;
    
    public LogStrategyProxy(@NotNull ILogStrategy strategy) {
        Intrinsics.checkNotNullParameter(strategy, "strategy");
        this.$$delegate_0 = strategy;
    }

    public void log(@NotNull String message) {
        Intrinsics.checkNotNullParameter(message, "message");
        this.$$delegate_0.log(message); }}Copy the code
  • Did you just have an Epiphany? It is also very simple to call, the code is as follows:
val logStrategyImpl = LogStrategyImpl()

LogStrategyProxy(logStrategyImpl).log("666666")
Copy the code
  • Finally, let’s look at the output log:
Test output: 666666Copy the code
  • I suddenly had a bold idea: how about overriding its interface methods using a class delegate? For example:
class LogStrategyProxy(strategy: ILogStrategy) : ILogStrategy by strategy {

    override fun log(message: String) {
        println("Test output" + message)
    }
}
Copy the code
  • I have done practice on this problem, there is no problem, everyone rest assured and bold.
Attribute to entrust
  • So having seen the class delegate above, you probably have some idea of delegate, but what is a property delegate? Class delegate is used to reduce implementation code, while attribute delegate is used to control Get and Set operations on variables
class XxxDelegate {

    // Give it a default value
    private var currentValue: String = "666666"

    operator fun getValue(thisRef: Any? , property:KProperty< * >): String {
        println("Test field name${property.name}The current value is$currentValue")
        return currentValue
    }

    operator fun setValue(thisRef: Any? , property:KProperty<*>, newValue: String) {
        currentValue = newValue
        println("Test field name${property.name}The current value is$currentValue" + ", new value$newValue")}}Copy the code
  • The following is an example of the code used:
var temp: String by XxxDelegate()
println("Test output" + temp)
temp = "55555"
println("Test output" + temp)
Copy the code
  • The log output is as follows:
Out: the variable named temp is accessed and the current value is 666666 system. out: the test output 666666 system. out: System. Out: the variable named temp was accessed and the current value is 55555. System. Out: the test output is 55555Copy the code
  • See if you get it here? ThisXxxDelegateThere are only two methods in the class, one isgetValueAnd the other one issetValueI don’t want to go into any more detail here, but I’ve got a pretty good idea of what it does from the way it’s named,var temp: String by XxxDelegate()Said thistempObject creation is given carte BlancheXxxDelegateThis class will do it.
Lazy to entrust
  • What is a lazy delegate? You know the slob in the singleton pattern? This is similar, except instead of writing static methods and locking mechanisms, we can write it like this:
val temp: String by lazy {
    println("Test variable initialized")
    return@lazy "666666"
}
Copy the code
  • The call code is as follows:
println("Test begins.")
println("Test first output" + temp)
println("Test the second output" + temp)
println("End of test.")
Copy the code
  • The following logs are displayed:
Out: the test starts. System.out: the test variable is initializedCopy the code
  • Is it really like slacker style? But it’s a lot simpler, and we can use it in everyday developmentfindViewByIdIs the most suitable.
private val viewPager: ViewPager? by lazy { findViewById(R.id.vp_home_pager) }
Copy the code
  • Lazy delegate also provides several lazy loading modes to choose from,

    • LazyThreadSafetyMode. SYNCHRONIZED: synchronous mode, ensure that only a single thread can initialize the instance, this mode initialization time thread-safe, when there is no specified by lazy mode, this mode is the default.

    • LazyThreadSafetyMode. PUBLICATION: Concurrent mode, under the multithreading allows concurrent initialization, but only the first return value as the instance, this mode is thread-safe, and LazyThreadSafetyMode SYNCHRONIZED biggest difference is that this model in a multi-threaded concurrent access to the initialization of efficiency is the highest, Essentially, you trade space for time. Whichever thread executes faster returns the result first, and the results of other threads are discarded.

    • LazyThreadSafetyMode. NONE: normal mode, this mode will not use locking to limit multithreaded access, so is thread safe, so please do not use this in the case of multi-threaded concurrent.

  • The specific way to use it is also very simple, as follows:

val temp: String by lazy(LazyThreadSafetyMode.NONE) {
    println("Test variable initialized")
    return@lazy "666666"
}
Copy the code
  • It is also important to note that variables using lazy delegates must be declared val (immutable), since it can only be assigned once.

Extension function

  • The extension functions were created by Kotlin to simplify the writing of some code, including let, with, run, apply, and Also
Let the function
  • Within the function block, the object can be referred to by IT. Returns the last line of the function block or specifies a return expression

  • General writing

fun main(a) {
    val text = Android Wheels
    println(text.length)
    val result = 1000
    println(result)
}
Copy the code
  • Let write
fun main(a) {
    val result = Android Wheels.let {
        println(it.length)
        1000
    }
    println(result)
}
Copy the code
  • The most common scenario is to use the let function to handle the need for a nullable object to be uniformly nulled
videoPlayer? .setVideoView(activity.course_video_view) videoPlayer? .setControllerView(activity.course_video_controller_view) videoPlayer? .setCurtainView(activity.course_video_curtain_view)Copy the code
videoPlayer? .let { it.setVideoView(activity.course_video_view) it.setControllerView(activity.course_video_controller_view) it.setCurtainView(activity.course_video_curtain_view) }Copy the code
  • Or you need to specify that a variable is available in a particular scope
With the function
  • The previous functions are used slightly differently because they do not exist as extensions. It takes an object as an argument to a function. Within a function block, you can refer to the object by this, return the last line of the function block, or specify a return expression

  • Define the Person class

class Person(var name : String, var age : Int)
Copy the code
  • General writing
fun main(a) {
    var person = Person(Android Wheels.100)
    println(person.name + person.age)
    var result = 1000
    println(result)
}
Copy the code
  • With writing
fun main(a) {
    var result = with(Person(Android Wheels.100)) {
        println(name + age)
        1000
    }
    println(result)
}
Copy the code
  • When it is applicable to call multiple methods of the same class, the class name can be omitted and the method of the class can be directly called. It is often used in AndroidRecyclerView.onBinderViewHolderThe attributes of the data model are mapped to the UI
override fun onBindViewHolder(holder: ViewHolder, position: Int){
    valitem = getItem(position)? :return
    holder.nameView.text = "Name:${item.name}"
    holder.ageView.text = "Age:${item.age}"
}
Copy the code

override fun onBindViewHolder(holder: ViewHolder, position: Int){
    valitem = getItem(position)? :return
    with(item){
        holder.nameView.text = "Name:$name"
        holder.ageView.text = "Age:$age"}}Copy the code
The run function
  • The run function takes only a lambda function as an argument and returns either the value of the last line or the expression of the specified return

  • General writing

var person = Person(Android Wheels.100)
println(person.name + "+" + person.age)
var result = 1000
println(result)
Copy the code
  • The run way
var person = Person(Android Wheels.100)
var result = person.run {
    println("$name + $age")
    1000
}
println(result)
Copy the code
  • Applies to let, with functions in any scenario. Because run is a combination of let and with, it makes up for the fact that let must use it arguments instead of objects in the function body. Like with, run can be omitted to directly access the public attributes and methods of instances. On the other hand, it makes up for the nullation problem of the object passed in by the with function. In the run function, nullation can be done just like the LET function, which is simplified by onBindViewHolder
override fun onBindViewHolder(holder: ViewHolder, position: Int){
    valitem = getItem(position)? :return
    holder.nameView.text = "Name:${item.name}"
    holder.ageView.text = "Age:${item.age}"
}
Copy the code

override fun onBindViewHolder(holder: ViewHolder, position: Int){
    valitem = getItem(position)? :returnitem? .run { holder.nameView.text ="Name:$name"
        holder.ageView.text = "Age:$age"}}Copy the code
The apply function
  • Structurally, apply and run are similar, except that they return different values. Run returns the value of the last line of code as a closure, whereas Apply returns the object itself

  • General writing

val person = Person(Android Wheels.100)
person.name = "HJQ"
person.age = 50
Copy the code
  • The apply of writing
val person = Person(Android Wheels.100).apply {
    name = "HJQ"
    age = 50
}
Copy the code
  • The function is similar to run except that it returns the value of the object itself, whereas run is a closure that returns the value of the last line. Because of this difference, it applies to a slightly different scenario than the run function. Apply is used when an object instance is initialized to assign values to attributes in the object. This can also be used when dynamically inflating an XML View requires binding data to the View, which is very common. In particular, we will need some data models to be converted to View Models in the process of instantiation
mRootView = View.inflate(activity, R.layout.example_view, null)
mRootView.tv_cancel.paint.isFakeBoldText = true
mRootView.tv_confirm.paint.isFakeBoldText = true
mRootView.seek_bar.max = 10
mRootView.seek_bar.progress = 0
Copy the code
  • The code after using the Apply function looks like this
mRootView = View.inflate(activity, R.layout.example_view, null).apply {
   tv_cancel.paint.isFakeBoldText = true
   tv_confirm.paint.isFakeBoldText = true
   seek_bar.max = 10
   seek_bar.progress = 0
}
Copy the code
  • Multi – level short call problem
if (sectionMetaData == null || sectionMetaData.questionnaire == null || sectionMetaData.section == null) {
    return;
}
if(sectionMetaData.questionnaire.userProject ! =null) {
    renderAnalysis();
    return;
}
if(sectionMetaData.section ! =null && !sectionMetaData.section.sectionArticles.isEmpty()) {
    fetchQuestionData();
    return;
}
Copy the code
  • Kotlin’s Apply function optimization
sectionMetaData? .apply {SectionMetaData is operated on when the sectionMetaData object is not empty}? .questionnaire? .apply {// Take the questionnaire when the questionnaire object is not free}? .section? .apply {// Section is operated on when the section object is not empty}? .sectionArticle? .apply {// Operate sectionArticle when the sectionArticle object is not empty

}
Copy the code
The braking function
  • Let returns the value of the last row in the body of the function. If the last row is empty, it returns a default value of type Unit. The also function returns the object itself
fun main(a) {
    val result = Android Wheels.let {
        println(it.length)
        1000
    }
    println(result) // Print: 1000
}
Copy the code
fun main(a) {
    val result = Android Wheels.also {
        println(it.length)
    }
    println(result) // Print: Android Wheels
}
Copy the code
  • This applies to any scenario in which a let function is used. Also is similar to a let function, except that the last value returned by a let function is the last line, whereas also returns the current object. Can be used for multiple extension function chain calls

Operator overloading

  • Using operators in Kotlin will also eventually call the corresponding methods of the object. We can override these methods to make the object support the operators. We won’t show the code here
The operator A method is called
+a a.unaryPlus()
-a a.unaryMinus()
! a a.not()
The operator A method is called
a++ a.inc()
a– a.dec()
The operator A method is called
a + b a.plus(b)
a – b a.minus(b)
a * b a.times(b)
a / b a.div(b)
a % b a.rem(b), a.mod(b) (deprecated)
a.. b a.rangeTo(b)
The operator A method is called
a in b b.contains(a)
a ! in b ! b.contains(a)
The operator A method is called
a[i] a.get(i)
a[i, j] a.get(i, j)
a[i_1, …, i_n] a.get(i_1, … , i_n)
a[i] = b a.set(i, b)
a[i, j] = b a.set(i, j, b)
a[i_1, …, i_n] = b a.set(i_1, … , i_n, b)
The operator A method is called
a() a.invoke()
a(i) a.invoke(i)
a(i, j) a.invoke(i, j)
a(i_1, … , i_n) a.invoke(i_1, … , i_n)
The operator A method is called
a += b a.plusAssign(b)
a -= b a.minusAssign(b)
a *= b a.timesAssign(b)
a /= b a.divAssign(b)
a %= b a.remAssign(b), a.modAssign(b) (deprecated)
The operator A method is called
a == b a? .equals(b) ? : (b === null)
a ! = b ! (a? .equals(b) ? : (b === null))
The operator A method is called
a > b a.compareTo(b) > 0
a < b a.compareTo(b) < 0
a >= b a.compareTo(b) >= 0
a <= b a.compareTo(b) <= 0

Next up:Kotlin for all: Special article on coroutines

In addition, I recommend an open source project written in Kotlin language, you can have a look:AndroidProject-Kotlin

Android technology to share QQ group: 78797078