Kotlin announces a super feature

Keywords: Android Algorithm

Kotlin 1.5 announced a blockbuster feature   value class, which is a very practical feature to improve code readability and performance, because the compiler will further optimize it and reduce the creation of objects.

With the continuous improvement of Kotlin, a series of features have emerged   inner class  ,  data class  ,  sealed class  , sealed interface  , inline class  , value class   wait

Today's article mainly analyzes   inline class   and   value class.

Through this article, you will learn the following:

  • What is an inline function?
    • When do you use inline functions?
  • What is?   inline class  ?
  • What is?   value class  ?
    • value class   Cannot be inherited
    • value class   Interface can be implemented
    • When the passed object is empty, the inline effect is lost
    • value class   Prohibited use  ===  have access to  ==
  • inline class   and   value class   What's the difference?
  • How does Java call functions that accept inline classes?

Inline function

Passed in Kotlin   inline-functions   Inline function is used to realize function inlining. The function of inlining is to improve operation efficiency and the call is called   inline   Modifier function will put the code in the method body where it is called. Its main purpose is to improve performance and reduce the creation of objects. The inline function code is shown below.

fun testInline() {
    printByteCode()
}

inline fun printByteCode() {
    println("printByteCode")
}

// After compilation
public static final void testInline() {
  String var1 = "printByteCode";
  System.out.println(var1);
}

inline   The modified function applies to the following cases

  • inline   Modifiers are used to take a function as an argument to another function, such as a higher-order function   filter  ,  map  ,  joinToString   Or some independent functions   repeat
  • inline   Operators fit and   reified   Operators are used together
  • If the function body is short, use   inline   Operators improve efficiency

If used   inline   The modifier marks ordinary functions, and Android Studio will give a big warning, as shown in the figure below, to prevent   inline   Performance loss caused by operator abuse.

But if the function body is very short, you want to pass   inline   Operators to improve efficiency, but also want to eliminate the warning, you can go to check   Kotlin skills and analysis that few people know (3)   "Use of Kotlin annotations in projects" in the article.

Inline class

Inline classes are an overlooked and very useful feature. Sometimes necessary business logic needs to encapsulate basic data types, strings and other parameters in a Model, and then encapsulate some methods in the Model to check and verify this parameter.

After the parameters are encapsulated, a wrapper object needs to be created. The creation of objects is allocated in the heap. In the case of a large amount of data, the loss of performance is also very large, such as memory occupation, runtime efficiency, frequent creation of objects, resulting in the Caton problem caused by GC recycling a large number of objects, and so on.

The runtime JVM will optimize the basic data type, String, etc., but if these parameters are encapsulated in a class, the wrapper class will not do any processing and will still create objects in the heap.

Therefore, in order to reduce performance loss and avoid object creation, Kotlin introduced an inline class   inline-classes . An inline class can only pass in one parameter in the constructor. The parameter needs to be declared with val. after compilation, it will be replaced with the passed in value. The code is as follows.

inline class User(val name: String)

fun testInline() {
    println(User("DHL"))
}

// After compilation
public static final void testInline() {
  System.out.println("DHL");
}

As you can see, the compiled Java code does not create additional objects. The objects created in Kotlin are replaced with the values passed in.

How does Java call functions that accept inline classes

Declare a function in Kotlin and   inline class   Pass it as a parameter. After compiling into Java code, the function name will be confused. The code is as follows.

inline class User(val name: String) 

fun login(user: User) {
}

// Compiled code
public static final void login_FY_U7ig/* $FF was: login-FY_U7ig*/(@NotNull String user) {
}

The compiled function name will be scrambled, for example   login-FY_U7ig. However, there is a problem. Java cannot call functions that accept inline classes. The code is as follows.

inline class User(val name: String) 

fun login(user: User) {
}

fun login(passwd: String) {
}

// Kotlin compilation is normal
login(User("DHL"))
login("ByteCode")

// Java compilation failed
MainKt.login("DHL");

The call can be compiled in Kotlin, because the inline causes cause the Java call to fail, as shown in the following figure.

In order to be called normally in Java, annotations are added  @ JvmName   Change the function name to solve this problem. The code is as follows.

inline class User(val name: String) 

@JvmName("loginWithName")
fun login(user: User) {
}

fun login(passwd: String) {
}

// Kotlin compilation is normal
login(User("DHL"))
login("ByteCode")

// Java compilation is normal
MainKt.loginWithName("DHL");

So whether it's   Inline classes   still   Value classes   If not added  @ JvmName   Annotation, there will be this problem. The purpose of confusing the generated function names is to prevent method overload conflicts or accidental Java calls.

Inline classes   It was introduced in Kotlin 1.3. It entered the stable version in Kotlin 1.5 and was abandoned   inline   Modifier, introduced   Value classes.

What is Value classes

Inline classes   yes   Value classes   A subset of,   Value classes   than   Inline classes   Will get more optimization at this stage   Value classes   and   Inline classes   Similarly, only one parameter can be passed in the constructor, and the parameter needs to be   Val declares that multiple parameters can be added to the constructor in the future, but each parameter needs to be used   val   The official statement is shown in the figure below.

If you support adding multiple parameters in the future, it will be used more and more widely.

After upgrading to Kotlin 1.5, Inline classes   Will be deprecated, as shown in the figure below, and the compiler will give a warning.

According to the prompt, the only thing that needs to be changed at present is the syntax   inline   Replace with   value, and then add  @ JvmInline   Just annotate.

@JvmInline
value class User(val name: String)

Compiled effects and   Inline classes   Is the same, so the following cases will use   Value classes.

Value classes cannot be inherited

because   value class   It will be added after compilation   fianl   Modifier, so it cannot be inherited, nor can it inherit other classes, as shown in the following figure.

Value classes can implement interfaces

although   value class   You cannot inherit other classes, but you can implement the interface. The code is as follows.

interface LoginInterface

@JvmInline
value class User(val name: String) : LoginInterface

fun testInline() {
    println(User("DHL"))
}

// Compiled code
public static final void testInline() {
  User var0 = User.box-impl(User.constructor-impl("DHL"));
  System.out.println(var0);
}

public static final User box_impl/* $FF was: box-impl*/(String v) {
  return new User(v);
}

As the code shows, when   value class   When implementing the interface, the inline effect is lost and will still be created in the heap   User   Object has no inline effect except when implementing the interface. When the object is empty, the inline effect will also be lost.

When the passed object is empty, the inline effect will be lost

When the parameter of the constructor is the basic data type, and the passed parameter   value class   When the object of is empty, the inline effect will be lost. The code is as follows.

@JvmInline
value class User(val age: Int)

fun login(user: User?): Int = user?.age ?: 0

fun testInline() {
    println(login(User(10)))
}

// Compiled code
public static final int login_js0Jwf8/* $FF was: login-js0Jwf8*/(@Nullable User user) {
  return user != null ? user.unbox-impl() : 0;
}

public static final void testInline() {
  int var0 = login-js0Jwf8(User.box-impl(10));
  System.out.println(var0);
}

public static final User box_impl/* $FF was: box-impl*/(int v) {
  return new User(v);
}

As you can see, it will still be created in the heap   User   Object, the inline effect is lost because   Int   Cannot convert directly to   null.

When the constructor parameter is   String, and passed parameters   value class   When the object of is empty, the inline effect will not be affected. The code is as follows.

@JvmInline
value class User(val name: String)

fun login(user: User?): String = user?.name ?: ""

fun testInline() {
    println(login(User("DHL")))
}

// Compiled code
public static final String login_js0Jwf8/* $FF was: login-js0Jwf8*/(@Nullable String user) {
    // ......
  return var10000;
}

public static final void testInline() {
    String var0 = login-js0Jwf8("DHL");
    System.out.println(var0);
}

The compiled Java code does not create an object and passes it to the method   login   Parameters of   User   Is replaced by the value passed in   String.

Value class prohibited = = = available==

Operators in Kotlin  ==  and  ===  For more information, you can go to   Uncover = = and in Kotlin=== , here is a brief introduction.

Kotlin provides two ways to compare objects.

  • Are the structures of the comparison objects equal(  ==  perhaps   equals  )

    Operators in Kotlin  ==  Equivalent to   equals   It is used to compare whether the structures of objects are equal. In many cases, it is used  ==, Because for floating-point types Float and Double, their implementation methods   equals   Does not comply with IEEE 754 floating point standard.

  • Compare whether references to objects are equal(  === )

    Operators in Kotlin  ===  It is used to compare whether the reference of an object points to the same object. If it is a basic data type at runtime  ===  Equivalent to  ==.

among   value class   It is special. Operators are prohibited  ===, As shown in the figure below, the compiler will give a warning.

Because operators  ===  It is used to compare whether the references of objects are equal. Because of inlining, they will eventually be replaced with the passed in value.

But operator  ==  Is unaffected, operator  ==  It is used to compare whether the structures of objects are equal. The code is as follows.

fun testInline() {
    val u1 = User("DHL")
    val u2 = User("DHL")
    println(u1 == u2)
}

// Compiled code
public static final void testInline() {
  String u1 = "DHL";
  String u2 = "DHL";
  boolean var2 = User.equals-impl0(u1, u2);
  System.out.println(var2);
}

If it's helpful, a compliment is my greatest encouragement

More code, more articles

Posted by Helljumper on Tue, 16 Nov 2021 04:59:30 -0800