An article to understand the extension method of kotlin

  • 2021-11-10 11:02:04
  • OfStack

Usage

kotlin extension function is another 1 killer function, can not modify the source code on the basis of the ability to expand some classes, easy to develop.

For example, here is a demonstration of adding a method to String to get the first element.


fun String.first(): Char {
  if (isEmpty()) {
    throw NoSuchElementException("String is empty")
  }
  return this[0]
}

fun main(args: Array<String>) {
  println("Hello,World".first())
}

An extra note here is that the extension function's method body can directly access the variables of the extension object public. For example, in the above method, we can also write this:


fun String.first(): Char {
  if (length < 1) {
    throw NoSuchElementException("String is empty")
  }
  return this[0]
}

You can access the extension object within the method through this, where you get the first character through this [0].

Under in hood

It looks great, but his principle is very simple. We should always remember that kotlin JVM is based on JVM development, kotlin source code will eventually become bytecode and then run. When you encounter something you don't understand syntax, you can directly decompile bytecode, or Decompile into Java method, and you can gain insight into the mystery inside.

After we turn the above code, Decompile into Java, we can discover the secret inside.


public static final char first(@NotNull String $this$first){
  Intrinsics.checkParameterIsNotNull($this$first, "$this$first");
  if ($this$first.length() < 1) {
   throw (Throwable)(new NoSuchElementException("String is empty"));
  } else {
   return $this$first.charAt(0);
  }
}

Originally, it generated a method of public static final, but this generation is the syntax sugar provided by kotlin, which helps us complete. Seeing this code also explains why the public members of the extension object can be accessed inside the extension object method.

Overloading and polymorphism

Can extension methods be inherited or overloaded? Let's look at an example


open class Animal
class Dog : Animal()

fun Animal.desc() = "Animal"
fun Dog.desc() = "Dog"

fun main(args: Array<String>) {
  println(Dog().desc())
  var animal: Animal = Dog()
  println(animal.desc())
}

// output:
// Dog
// Animal

If the extension method can be overloaded, then Dog should be output both times. Let's look at the truth like the previous method 1.


@NotNull
public static final String desc(@NotNull Animal $this$desc) {
  Intrinsics.checkParameterIsNotNull($this$desc, "$this$desc");
  return "Animal";
}

@NotNull
public static final String desc(@NotNull Dog $this$desc) {
  Intrinsics.checkParameterIsNotNull($this$desc, "$this$desc");
  return "Dog";
}

You can see that two desc methods are actually generated, and the parameters in them are fixed, so the call of this method is only related to the extension object itself, and it has been determined at compile time that there is no polymorphism.

Extended attribute

This is a very magical setting, kotlin does not really add an attribute to the extension object, but only provides a syntax sugar, what do you mean? Let's look at the following example in detail.


var String.first: Char
  get() {
    if (isEmpty()) {
      throw NoSuchElementException( " String is empty " )
    }
    return this[0]
  }
  set(value) {
    println( " set value to $value " )
  }

fun main() {
   " Hello, World " .first =  ' G'
  println( " Hello,World " .first)
}

We extended the properties of kotlin and added an first. We can add get and set methods to this so-called first attribute, note what is called. We can then call the set and get methods with = and., as in the main method. But in fact, the first attribute was not generated in the end. Let's look at the decompiled code.


public static final Char getFirst(@NotNull String $this$first) {
  Intrinsics.checkParameterIsNotNull($this$first, "$this$first");
  CharSequence var1 = (CharSequence)$this$first;
  boolean var2 = false;
  if (var1.length() == 0) {
   throw (Throwable)(new NoSuchElementException("String is empty"));
  } else {
   return $this$first.charAt(0);
  }
}

public static final void setFirst(@NotNull String $this$first, char value) {
  Intrinsics.checkParameterIsNotNull($this$first, "$this$first");
  String var2 = "set value to " + value;
  boolean var3 = false;
  System.out.println(var2);
}

See, in fact, only two methods, setFirst and getFirst, have been added, and no actual attributes have been added. This is also the grammar sugar provided by kotlin. Eat sugar, but be careful of tooth decay!


Related articles: