Usage of kotlin extension function and extension attribute

Kotlin can extend the new functionality of a class without inheriting the class or using design patterns such as decorators. This is done through special declarations called extensions. For example, you can write a new function for a class from a third-party library that you cannot modify. This new function is like the original function of the original class, which can be called with ordinary methods. This mechanism is called extension function. In addition, there are extended properties that allow you to add new properties to an existing class.

preface

As an Android Developer, we often encounter such a scenario that we need to convert the value in DP into px. At this time, we often write a utils class, for example

public class Utils {

 public static float dp2px(int dpValue) {
  return (0.5f + dpValue * Resources.getSystem().getDisplayMetrics().density);
 }
}

Use utils.dp2px (100) directly in the code,

val dp2px = Utils.dp2px(100)

If implemented by means of kotlin extension function, how will it be called?

val dp2px = 100.dp2px()

Isn't it surprising that 100, as an int, directly calls a dp2px method. If you look in the source code, there is no method. Instead of moving the source code, we use the extension function to add a method to int.

fun Int.dp2px(): Float {
 return (0.5f + this * Resources.getSystem().displayMetrics.density)
}

spread function

Let's take another example 🌰, There is a person class as follows

class Person(val name: String) {

 fun eat() {
  Log.i(name,"I'm going to eat")
 }

 fun sleep() {
  Log.i(name,"I'm going to sleep")
 }

}

It has two methods, one is eat and the other is sleep. If called, the corresponding log will be printed respectively. We don't want to move the person class now, but we want to add a new method to it. What should we do. We can create a new file personextensions.kt, and then through the code implementation, we can add a drink method for the person class.

fun Person.drink() {
 Log.i("Person","${this.name}: I'm going to drink")
}

To declare an extension function, we need to prefix it with a receiver type, that is, the extended type. Above, we use person as the receiving type of an extension function to expand the drink method. We call this in its method, which refers to the current Person object that calls this extension method. This

Extension function calls are the same as ordinary methods. However, you will find that the IDE display method is a little different in color.

It can also be seen that the ordinary method is different from our expansion function. Let's take a look at the actual implementation of the extension function.

In Android studio, we can view the bytecode of kotlin file, and then decompile it into Java code. After we convert the person extension function into Java code, it is as follows.

@Metadata(
 mv = {1,1,15},bv = {1,3},k = 2,d1 = {"\u0000\f\n\u0000\n\u0002\u0010\u0002\n\u0002\u0018\u0002\n\u0000\u001a\n\u0010\u0000\u001a\u00020\u0001*\u00020\u0002¨\u0006\u0003"},d2 = {"cook","","Lcom/chaochaowu/kotlinextension/Person;","app_debug"}
)
public final class PersonExtensionsKt {
 public static final void cook(@NotNull Person $this$cook) {
  Intrinsics.checkParameterIsNotNull($this$cook,"$this$cook");
  Log.i("Person",$this$cook.getName() + ": I'm going to cook");
 }
}

I thought of it. It used to be a static method declared by static final. Its input parameter is a person type, that is, our previous receiving type. Can it be called in Java code?

PersonExtensionsKt.cook(new Person("Bob"));

There was no error! It can be seen that the so-called extension function does not really add a method to the class, but is implemented through the static method of external files. In fact, it is the same as the utils class.

Because a person is passed into the method as an input parameter, we can operate on the person object in the method, which is why we can use this to access the person attribute in the extension method.

Let's look at a special example.

val s: String? = null
s.isNullOrEmpty()

In the above code, the value of S is null. We called a method with null. Will this report an error? According to previous experience, when a null calls a method, it will inevitably report an exception of null pointer, but the above code will not collapse. Why?

Isnullorempty is actually charsequence? An extension method of, we can take a look at its source code.

@kotlin.internal.InlineOnly
public inline fun CharSequence?.isNullOrEmpty(): Boolean {
 contract {
  returns(false) implies (this@isNullOrEmpty != null)
 }

 return this == null || this.length == 0
}

We don't need to pay attention to the contract method, and it doesn't affect. It's mainly about the sentence return this = = null | this. Length = = 0. It first determines whether this is empty, and then determines the length of this. According to the essence of the extension function we mentioned above, we can well understand why null can call this method. Because this is what happens when the above code is converted to Java code.

 public static final boolean isNullOrEmpty(@Nullable CharSequence $this$isNullOrEmpty) {
  int $i$f$isNullOrEmpty = 0;
  return $this$isNullOrEmpty == null || $this$isNullOrEmpty.length() == 0;
 }

When we call this extension method with null, we actually pass null into this method as a parameter. First judge whether the parameter is null, and then judge the next step. Of course, it won't crash.

Extensions cannot really modify the classes they extend. By defining an extension, you do not insert a new member into a class, but you can call the new function with a dot expression through a variable of this type and pass itself as a parameter.

Extended properties

The extension attribute is similar to the extension function. Take the example of person above. We slightly modify the person class and add the birthdayyear field to indicate its year of birth.

class Person(val name: String,val birthdayYear: Int) {

 fun eat() {
  Log.i(name,"I'm going to sleep")
 }

}

Now we need to add the age attribute for person, but how to implement it without changing the person class. Like the extension function, it is declared in other files as follows.

const val currentYear = 2019

val Person.age: Int
 get() = currentYear - this.birthdayYear

We calculate the age of person by subtracting the birthday year from the current year. As you can see, age is an attribute, not a method. In this way, we add an extended attribute for person. You can see what it looks like after it is converted into Java code, which is no different from the extension function.

@Metadata(
 mv = {1,d1 = {"\u0000\u0010\n\u0000\n\u0002\u0010\b\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0003\"\u000e\u0010\u0000\u001a\u00020\u0001X\u0086T¢\u0006\u0002\n\u0000\"\u0015\u0010\u0002\u001a\u00020\u0001*\u00020\u00038F¢\u0006\u0006\u001a\u0004\b\u0004\u0010\u0005¨\u0006\u0006"},d2 = {"currentYear","age","getAge","(Lcom/chaochaowu/kotlinextension/Person;)I","app_debug"}
)
public final class PersonExtensionsKt {
 public static final int currentYear = 2019;

 public static final int getAge(@NotNull Person $this$age) {
  Intrinsics.checkParameterIsNotNull($this$age,"$this$age");
  return 2019 - $this$age.getBirthdayYear();
 }
}

We declare a Val above. Of course, we can also declare a VaR, but for VaR, we need to define its get and set methods at the same time. Since the extension does not actually insert members into the class, the field behind the scenes is invalid for the extension property. This is why extended properties cannot have initializers. Their behavior can only be defined by explicitly provided getters / setters.

summary

In Java, when we want to extend a class, we often inherit the class or implement it with a design pattern similar to decorator pattern. Kotlin extension function and extension attribute provide a new idea for this requirement, and can also be used as another choice of utils class, which is worth trying.

The above is the whole content of this article. I hope it will help you in your study, and I hope you will support us a lot.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>