preface

Kotlin makes it easier to interact with Java (PY) by providing some annotation parameters that make calling Kotlin easier and friendlier for Java.

Kotlin official annotation address

Today we’ll learn and understand these common notes:JvmDefault JvmField JvmMultifileClass JvmName JvmOverloads JvmStatic Strictfp Synchronized Volatile Transient

JvmDefault

Specifies that JVM default methods are generated for non-abstract Kotlin interface members. The use of this annotation requires the compiler parameter to be specified: -xJVM-default =enable or -xJVM-default =compatibility.

  • -Xjvm-default=enable: For each@JvmDefaultMethod generates default methods in the interface. In this mode, use@JvmDefaultAnnotating an existing method may break binary compatibility because it will effectively be fromDefaultImplsClass to remove the method.
  • -Xjvm-default=compatibility: In addition to the default interface methods, generated compatibility accessDefaultImplsClass that calls methods that access the default interface through composition. In this mode, use@JvmDefaultAnnotation existing methods are binary compatible, but more methods are generated in bytecode.

Removing this annotation from the interface member changes the binary incompatibility in both modes.

Only JVM target bytecode version 1.8 (-JVM-target 1.8) or higher can generate default methods.


Let’s try this annotation out and see how it works. Okay

First, let’s look at the unannotated case:

interface Animal {
  var name: String?
  var age: Int?
  fun getDesc(a) = name + "This year already." + age + "Old ~"

}

class Dog(override varname: String? .override var age: Int?). : Animalfun main(args: Array<String>? {
  Dog("White".3).getDesc()
}
Copy the code

Bytecode to Java code looks like this:


public interface Animal {
   @Nullable
   String getName(a);

   void setName(@Nullable String var1);

   @Nullable
   Integer getAge(a);

   void setAge(@Nullable Integer var1);

   @NotNull
   String getDesc(a);

   public static final class DefaultImpls {
      @NotNull
      public static String getDesc(Animal $this) {
         return $this.getName() + "This year already." + $this.getAge() + "Old ~"; }}}public final class Dog implements Animal {
   @Nullable
   private String name;
   @Nullable
   private Integer age;

   @Nullable
   public String getName(a) {
      return this.name;
   }

   public void setName(@Nullable String var1) {
      this.name = var1;
   }

   @Nullable
   public Integer getAge(a) {
      return this.age;
   }

   public void setAge(@Nullable Integer var1) {
      this.age = var1;
   }

   public Dog(@Nullable String name, @Nullable Integer age) {
      this.name = name;
      this.age = age;
   }

   @NotNull
   public String getDesc(a) {
      return Animal.DefaultImpls.getDesc(this); }}public final class JvmKt {
   public static final void main(@Nullable String[] args) {(new Dog("White".3)).getDesc(); }}Copy the code

As you can see from the above code, when we call the default method of the interface, we are actually calling the method of an anonymous static inner class.

Kotlin creates a static inner class that calls DefaultImpls to store the default implementation of methods that are static, and uses the “Self” sink type to simulate methods that belong to objects. Then, for each type that extends the interface, if the type does not implement the method itself, Kotlin will connect the method to the default implementation through a call at compile time.

Such an implementation has both advantages and disadvantages. The advantage is that it can provide the power of specific methods on interfaces on JVMS prior to Java 8. The disadvantages are:

  • It is incompatible with Java processing, resulting in messy interoperability. We can call it directly in Java codeDefaultImplsClass, this is an amazing thing….
  • One of the main reasons for the existence of default methods in Java8 is the ability to avoid having to touch every subclass (for example, addCollection.stream()) to add methods to the interface. The Kotlin implementation does not support this because the default call must be generated on each concrete type. Adding new methods to the interface causes each implementer to have to be recompiled.

To solve this problem,Kotlin launched @jVMDefault to optimize the situation. Now let’s see what happens with annotations: Gradle files with compiled configurations added

-jvm-target=1.8 -Xjvm-default=enable
Copy the code

Kotlin code

interface Animal {
 var name: String?
 var age: Int?
 @JvmDefault
 fun getDesc(a) = name + "This year already." + age + "Old ~"

}

class Dog(override varname: String? .override var age: Int?). : Animalfun main(args: Array<String>? {

 Dog("White".3).getDesc()
}
Copy the code

Corresponding Java code

public interface Animal {
   @Nullable
   String getName(a);

   void setName(@Nullable String var1);

   @Nullable
   Integer getAge(a);

   void setAge(@Nullable Integer var1);

   @JvmDefault
   @NotNull
   default String getDesc(a) {
      return this.getName() + "This year already." + this.getAge() + "Old ~"; }}public final class Dog implements Animal {
   @Nullable
   private String name;
   @Nullable
   private Integer age;

   @Nullable
   public String getName(a) {
      return this.name;
   }

   public void setName(@Nullable String var1) {
      this.name = var1;
   }

   @Nullable
   public Integer getAge(a) {
      return this.age;
   }

   public void setAge(@Nullable Integer var1) {
      this.age = var1;
   }

   public Dog(@Nullable String name, @Nullable Integer age) {
      this.name = name;
      this.age = age; }}public final class JvmKt {
   public static final void main(@Nullable String[] args) {(new Dog("White".3)).getDesc(); }}Copy the code

We can see that the annotation eliminates the anonymous static inner class to bridge the implementation of the default method, so that Java calls the default method of the Kotlind interface are basically the same as the default method of the Java interface.

Note that in addition to changing the compiler flags, compatibility modes were added using Kotlin 1.2.50. The compatibility flag (-xJVM-default = Compatibility) is designed to preserve binary compatibility with existing Kotlin classes while still being able to move to Java 8-style default methods. This flag is particularly useful when considering other generated projects that point to static bridging methods.

To achieve this, the Kotlin compiler uses the class file trick Invokespecial to call the default interface methods while still keeping the DefaultImpls bridge class. Let’s take a look at what it looks like:

public interface Animal {
   @JvmDefault
   @NotNull
   default String getDesc(a) {
      return "Meow, meow, meow, meow.";
   }

   public static final class DefaultImpls {
      @NotNull
      public static String getDesc(Animal $this) {
         return $this.getDesc(); }}}// In the case of new compilation
public final class Dog implements Animal {}In some other projects, compilation already exists
public final class OldDog implements Animal {
   @NotNull
   public String getDesc(a) {
      return Animal.DefaultImpls.getDesc(this); }}Copy the code

There is a good decompression here, especially since this is not valid Java syntax. Here are some DOS and don ‘ts:

  • Generate the default method on the interface just as we did when we first used itenable
  • Newly compiled classes, such asDog, will directly use the Java 8-style default interface.
  • likeOldDogSuch existing compiled code still works at the binary level because it points to the DefaultImpls class.
  • theDefaultImplsThe implementation of a method cannot be represented in a real Java source because it is analogous to a call<init>or<super>; This method must call getDesc on the Animal interface on the provided instance. If it just calls “getDesc ()”, it will cause the old type (OldDog.getDesc() -> DefaultImpls.getDesc() -> OldDog.getDesc()) stack overflow. Instead, it must call the interface directly:OldDog.getDesc() -> DefaultImpls.getDesc() -> Animal.getDesc(), and only through interface methodsinvokespecialCall to complete.

JvmField

Causes the Kotlin compiler to no longer generate getters/setters for this field and treat it as a public field (public)

The usage scenarios are as follows:

Kotlin code

class Bean(
  @JvmField
  varname:String? .var age:Int
)
Copy the code

Corresponding Java code

public final class Bean {
   @JvmField
   @Nullable
   public String name;
   private int age;

   public final int getAge(a) {
      return this.age;
   }

   public final void setAge(int var1) {
      this.age = var1;
   }

   public Bean(@Nullable String name, int age) {
      this.name = name;
      this.age = age; }}Copy the code

By contrast, annotated field property modifiers change from private to public


JvmName

The main purpose of this annotation is to tell the compiler the name of the generated Java class or method

The usage scenarios are as follows:

Koltin code

@file:JvmName("JavaClass")

package com.example.maqiang.sss

var kotlinField: String? = null
  // Change the name of the property's set method
  @JvmName("setJavaField")
  set(value) {
    field = value
  }

// Change the normal method name
@JvmName("JavaFunction")
fun kotlinFunction(a){}Copy the code

The corresponding Java code:

public final class JavaClass {
   @Nullable
   private static String kotlinField;

   @Nullable
   public static final String getKotlinField(a) {
      return kotlinField;
   }

   @JvmName(
      name = "setJavaField"
   )
   public static final void setJavaField(@Nullable String value) {
      kotlinField = value;
   }

   @JvmName(
      name = "JavaFunction"
   )
   public static final void JavaFunction(a) {}}Copy the code

Java calls kotlin code

public class JavaJvm{
  public static void main(String[] args) {
    // Class names and methods are modified after annotation
    JavaClass.JavaFunction();
    JavaClass.getKotlinField();
    JavaClass.setJavaField("java"); }}Copy the code

This annotation is used to deal with compatibility issues after various class names are changed


JvmMultifileClass

This annotation lets the Kotlin compiler generate a multifile class with the top-level functions and attributes declared in the file as part of it, and the JvmName annotation provides the name of the corresponding multifile.

Usage Scenario analysis:

Kotlin code:

//A.kt
@file:JvmName("Utils")
@file:JvmMultifileClass
package com.example.maqiang.sss
fun getA(a) = "A"

//B.kt
@file:JvmName("Utils")
@file:JvmMultifileClass
package com.example.maqiang.sss
fun getB(a) = "B"
Copy the code

Java calls Kotlin’s top-level functions

public class JavaJvm {
  public static void main(String[] args) { Utils.getA(); Utils.getB(); }}Copy the code

We can see that the annotation combines the methods in the A and B files into one Utils class. This annotation eliminates the need to manually create A Utils class and makes adding methods to the Utils class more flexible and convenient


JvmOverloads

Tells the Kotlin compiler to generate an overload to replace the default parameter values for this function

The usage scenarios are as follows:

Kotlin code

@JvmOverloads
fun goToActivity(
  context: Context? , url:String? , bundle:Bundle? = null,
  requestCode: Int = - 1
){}Copy the code

Corresponding Java code

public final class AKt {
   @JvmOverloads
   public static final void goToActivity(@Nullable Context context, @Nullable String url, @Nullable Bundle bundle, int requestCode) {}// $FF: synthetic method
   // $FF: bridge method
   @JvmOverloads
   public static void goToActivity$default(Context var0, String var1, Bundle var2, int var3, int var4, Object var5) {
      if ((var4 & 4) != 0) {
         var2 = (Bundle)null;
      }

      if ((var4 & 8) != 0) {
         var3 = -1;
      }

      goToActivity(var0, var1, var2, var3);
   }

   @JvmOverloads
   public static final void goToActivity(@Nullable Context context, @Nullable String url, @Nullable Bundle bundle) {
      goToActivity$default(context, url, bundle, 0.8, (Object)null);
   }

   @JvmOverloads
   public static final void goToActivity(@Nullable Context context, @Nullable String url) {
      goToActivity$default(context, url, (Bundle)null.0.12, (Object)null);
   }
Copy the code

We can see that in order for Java to enjoy Koltin’s default parameter features, this annotation is used to generate the corresponding overloaded method.

The rule for overloading is sequential overloading, and only parameters with default values are reloaded.


JvmStatic

Use this annotation on a function, and the Kotlin compiler generates another static method

Using this annotation on a property, the Kotlin compiler generates additional setter and getter methods

This annotation eliminates the problem of Java calling Kotlin’s Companion Object without directly calling its static methods and properties.

Comparison of Usage Scenarios

Companion Object without annotations

class A {
  companion object {
    var string: String? = null
    fun hello(a) = "hello,world"}}Copy the code

Corresponding Java code

public final class A {
   @Nullable
   private static String string;
   public static final A.Companion Companion = new A.Companion((DefaultConstructorMarker)null);
   
   public static final class Companion {
      @Nullable
      public final String getString(a) {
         return A.string;
      }

      public final void setString(@Nullable String var1) {
         A.string = var1;
      }

      @NotNull
      public final String hello(a) {
         return "hello,world";
      }

      private Companion(a) {}// $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this(a); }}}Copy the code

We can see that when Java calls kotlin’s Companion object’s methods and properties, it needs Companion.

In the case of annotations in the Companion Object

class A {
  companion object {
    @JvmStatic
    var string: String? = null
    @JvmStatic
    fun hello(a) = "hello,world"}}Copy the code

Corresponding Java code

public final class A {
   @Nullable
   private static String string;
   public static final A.Companion Companion = new A.Companion((DefaultConstructorMarker)null);

   @Nullable
   public static final String getString(a) {
      A.Companion var10000 = Companion;
      return string;
   }

   public static final void setString(@Nullable String var0) {
      A.Companion var10000 = Companion;
      string = var0;
   }

   @JvmStatic
   @NotNull
   public static final String hello(a) {
      return Companion.hello();
   }

   public static final class Companion {
      / * *@deprecated* /
      // $FF: synthetic method
      @JvmStatic
      public static void string$annotations() {
      }

      @Nullable
      public final String getString(a) {
         return A.string;
      }

      public final void setString(@Nullable String var1) {
         A.string = var1;
      }

      @JvmStatic
      @NotNull
      public final String hello(a) {
         return "hello,world";
      }

      private Companion(a) {}// $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
Copy the code

As you can see, while the Companion static inner class still exists, Java can now call the corresponding static methods and properties directly.

Comparison of Java calls before and after annotations

public class JavaJvm {
  public static void main(String[] args) {
    // Before using annotations
    A.Companion.hello();
    A.Companion.getString();
    A.Companion.setString("hello,kotlin");
    // After using annotations
    A.hello();
    A.getString();
    A.setString("hello,kotlin"); }}Copy the code

Obvious annotations make the interaction between Java and Kotlin much friendlier


Strictfp

Marking the JVM methods generated from the annotation function as STRICtFP means that you need to limit the precision of floating point operations performed within the method for better portability.

Corresponding to strictFP keyword in Java

The usage scenarios are as follows:

// Can be used in constructors, getters/setters of properties, and normal methods
// The Target on the official website has a class, but the actual use of class does not annotate
class JvmAnnotation @Strictfp constructor() {
    var a: Float = 0.0f
        @Strictfp
        get() {
            return 1f
        }
        @Strictfp
        set(value) {
            field = value
        }
    @Strictfp
    fun getFloatValue(a): Float = 0.0f
    
}
Copy the code

Synchronized

The JVM method generated from an annotated function is marked as synchronized, which means that the method is protected from concurrent execution by multiple threads of the monitor that defines the instance of the method (or, in the case of static methods, classes).

Corresponds to the Synchronized keyword in Java

The usage scenarios are as follows

class JvmAnnotation {
    var syn: String = ""
        @Synchronized
        get() {
            return "test"
        }
        @Synchronized
        set(value) {
            field = value
        }
    @Synchronized
    fun getSynString(a): String = "test"
    
    fun setSynString(str:String){
        // Note that we are using an introverted function to lock the code block
        synchronized(this){
            println(str)
        }
    }

}
Copy the code

Volatile

Jvma-supported fields with annotated attributes are marked as volatile, which means that writes to the field are immediately visible to other threads.

Corresponds to the volatile keyword in Java

The usage scenarios are as follows

// The val variable cannot be annotated
@Volatile
var volatileStr: String = "volatile"
Copy the code

Transient

The JVM-supported field of the annotated property is marked transient, indicating that it is not part of the default serialization form of the object.

Corresponding to transient keyword in Java

The usage scenarios are as follows:

//:Serializable
data class XBean(
    valname: String? .val age: Int? .// Does not participate in serialization
    @Transient
    val male: Boolean = true
): Serializable

//Parcelize(experimental = true in Gradle)
@Parcelize
data class XBean(
    valname: String? .val age: Int? .// Does not participate in serialization
    @Transient
    val male: Boolean = true
)
Copy the code

These are some of the most commonly used comments in the daily development process. If you have any questions, please leave a comment