Before the order

What are annotations? To put it simply, annotation is a kind of annotation (mark, mark), without specific functional logic code. Annotations allow developers to embed supplementary information in source code without changing the original code and logic. Kotlin annotations are used exactly like Java, with slightly different syntax for declaring annotation classes. Java annotations are 100% Kotlin compatible.

Definition of annotations

Annotations can associate additional metadata to a declaration, which can then be accessed by reflection mechanisms or related source code tools.

A note declaring Kotlin

Kotlin’s declaration annotation syntax is very similar to a regular class declaration, but requires the annotation modifier to precede the class keyword. However, the Kotlin compiler forbids specifying class bodies for annotation classes because annotation classes are only used to define the structure of metadata associated with declarations and expressions.

#daqiKotlin.kt
annotation class daqiAnnotation
Copy the code

Java annotation declaration:

#daqiJava.java
public @interface daqiAnnotation {
}
Copy the code

Annotation constructor

Annotations can have constructors that accept arguments.

The annotated constructor allows the following types of arguments:

  • Types corresponding to Java native types (Int, Long, and so on)
  • string
  • Class (Foo: : class)
  • The enumeration
  • Other annotations
  • Array of the column type above.

Annotations are taken as arguments to the annotation constructor

When an annotation is an argument to another annotation, its name is not prefixed with the @ character:

annotation class daqiAnnotation(val str: String)

annotation class daqiAnnotation2(
    val message: String,
    val annotation: daqiAnnotation = daqiAnnotation(""))
Copy the code

Class as an argument to the annotation constructor

When you need to specify a class as an argument to an annotation, use the Kotlin class (KClass). The Kotlin compiler automatically converts this to a Java class so that Java code can see the annotations and parameters normally.

annotation class daqiAnnotation(val arg1: KClass<*>, val arg2: KClass<out Any>)

@daqiAnnotation(String::class, Int::class) class MyClass
Copy the code

After decompilating it, you can see that it is converted to the appropriate Java class:

@daqiAnnotation(
   arg1 = String.class,
   arg2 = int.class
)
public final class MyClass {
}
Copy the code

Note: Annotation parameters cannot have nullable types because the JVM does not support storing NULL as the value of an annotation attribute.

Kotlin’s meta-annotations

Like Java, Kotlin’s annotation classes are annotated using meta-annotations. The annotations used for other annotations are called meta-annotations and can be understood as the most basic annotations.

MustBeDocumented, Repeatable, Retention, and Target are defined in the Kotlin Standard Library.

@Target

The @target is used to specify the element type (class, function, attribute, expression, and so on) to which the annotation can be applied.

Check Target’s source code:

#Annotation.kt
@Target(AnnotationTarget.ANNOTATION_CLASS)
@MustBeDocumented
public annotation class Target(vararg val allowedTargets: AnnotationTarget)
Copy the code

An @target annotation can receive one or more enumerations of AnnotationTarget values at the same time:

Public enum class AnnotationTarget {// for classes (including enum classes), interfaces, object objects, and annotation classes, // only for annotation classes // Applies to generic type parameters (currently not supported) (JDK8) TYPE_PARAMETER, // applies to properties, // applies to fields (including enumerated constants and supported fields). FIELD, // LOCAL_VARIABLE, // VALUE_PARAMETER, // CONSTRUCTOR (primary and secondary constructors) CONSTRUCTOR, PROPERTY_GETTER; // PROPERTY_SETTER; // PROPERTY_SETTER; // PROPERTY_SETTER; // Applies to TYPE (such as the TYPE of the argument in the method) TYPE, // applies to EXPRESSION, // applies to file, which can be used with the file point target: (e.g. @file:JvmName)"daqiKotlin"SinceKotlin(SinceKotlin(SinceKotlin))"1.1")
    TYPEALIAS
}

Copy the code

Note: Java code unable to use Target for AnnotationTarget. The PROPERTY of the annotation. If you want this annotation to be used in Java, you can add an additional annotationTarget.field annotation.

@Retention

@retention Specifies the Retention policy for annotations.

Read the Retention source:

@Target(AnnotationTarget.ANNOTATION_CLASS)
public annotation class Retention(val value: AnnotationRetention = AnnotationRetention.RUNTIME)
Copy the code

An AnnotationRetention enumeration value can be received in the @Retention annotation:

Public enum class AnnotationRetention {// annotates only in the source code and will be discarded by the compiler. SOURCE, // Annotations will be recorded by the compiler in the class file but do not need to be retained by the JVM at runtime. BINARY, // Annotations will be recorded by the compiler in a class file and retained by the JVM at run time, so they can be read reflexively. (Default behavior) RUNTIME}Copy the code

Note: Java meta-annotations retain annotations in the.class file by default, but do not make them accessible at runtime. Most annotations need to exist at RUNTIME, so much so that Kotlin uses RUNTIME as the default for @Retention annotations.

@Repeatable

Allow the same annotation to be used multiple times on a single element;

Repeatable source code:

@Target(AnnotationTarget.ANNOTATION_CLASS)
public annotation class Repeatable
Copy the code

Note: in “try to use” @ the Repeatable, it must be specified in the Retention yuan notes for AnnotationRetention. The SOURCE can be repeated use, but the Java @ the Repeatable yuan notes is not the limit. See this article for an example of how to use Java @REPEATable meta annotations. Because @REPEATable is a new meta annotation introduced in Java 8 and Java 6 compatible Kotlin is a little bit incompatible with it?

@MustBeDocumented

Specifies that the annotation is part of the public API and should be included in the signature of the class or method displayed in the generated API documentation.

MustBeDocumented

@Target(AnnotationTarget.ANNOTATION_CLASS)
public annotation class MustBeDocumented
Copy the code

The missing @Inherited meta-annotation

Kotlin provides only four meta annotations, compared to five in Java. For the time being, Kotlin does not need to support @Inherited.

The @Inherited annotation indicates that annotation types can be Inherited from superclasses. There is an annotation type with @Inherited meta-annotations. When a user queries the annotation type in a class and there is no annotation of this type, the user tries to obtain the annotation type from the superclass of the class. Repeat this process until an annotation of this type is found, or until you reach the top of the class hierarchy (objects). If no superclass has an annotation of this type, the query will indicate that the related class has no such annotation. This annotation applies only to class declarations.

Predefined annotations by Kotlin

Kotlin, in the interest of good interoperability with Java, defines a series of annotations to carry some additional information for the compiler to do compatible conversions.

@JvmDefault

The default methods of the Kotlin interface generate bytecode for the default methods of Java 8

View source code:

@SinceKotlin("1.2")
@RequireKotlin("1.2.40", versionKind = RequireKotlinVersionKind.COMPILER_VERSION)
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
annotation class JvmDefault
Copy the code

As mentioned earlier in interfaces and classes, when you declare an interface with default methods in Kotlin, these “default methods” are often declared as abstract methods, and a DefaultImpls static inner class is also generated in that interface, in which static methods of the same name are defined to provide the default implementation.

One problem with this is that when a new default method is added to an old Kotlin interface, the Java class that implements the interface needs to re-implement the new interface method or it will compile and fail. The same default method, but against the intent of Java 8’s introduction of default methods. To do this, Kotlin provides the @jVMDefault annotation. For default methods annotated with @jVMDefault, the compiler compiles them as Java 8’s default interface.

#daqiKotlin.ktPublic interface daqiFunc() = println()"Default method with @jvmDefault")

    fun daqiFunc2() = println("Default method")}Copy the code
# Java file
public interface daqiInterface {
   @JvmDefault
   default void daqiFunc() {
      String var1 = "Default method with @jvmDefault";
      System.out.println(var1);
   }

   void daqiFunc2();

   public static final class DefaultImpls {
      public static void daqiFunc2(daqiInterface $this) {
         String var1 = "Default method"; System.out.println(var1); }}}Copy the code

The compiler raises an error when you add @jvmDefault directly. In this case, you need to configure the following parameters in Gradle:

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = ['-Xjvm-default = compatibility']
        //freeCompilerArgs = ['-Xjvm-default = enable']}}Copy the code

According to the comment at @jVMdefault, you can choose -xJVM-default = enable or -xJVM-default = compatibility during configuration. The difference between the two is:

  • -Xjvm-default = enablefromDefaultImplsRemove the corresponding method from the static inner class.
  • -Xjvm-default = compatibilityWill still be inDefaultImplsKeep corresponding methods in static inner classes to improve compatibility.

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

@JvmField

Instructs the Kotlin compiler not to generate a getter/setter for this property and to decorate it as public.

View source code:

@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
public actual annotation class JvmField
Copy the code

Kotlin declares properties that are private by default and provide setter/getter accessors to access them. @jVMField tells the compiler not to automatically create a setter/getter accessor for this property and to use the public modifier on it. (Used on properties of associated objects to generate static properties that are public qualified)

#daqiKotlin.kt
class Person{
    @JvmField
    val name:String = ""
}
Copy the code

Only one public object is declared:

# Java file
public final class Person {
   @JvmField
   @NotNull
   public final String name = "";
}
Copy the code

Note that this annotation can only be used for attributes that have a background field. It cannot be used for attributes that do not have a background field (e.g. extended attributes, delegate attributes, etc.). Because only properties that have hidden fields are converted to Java code do they have corresponding Java variables.

The Kotlin attribute must have one of the following conditions:

  • Properties that use default getters/setters must have hidden fields. For the var property, as long as one of the getters/setters uses the default implementation, the background field is generated.
  • The field property is used in the custom getter/setter.

@JvmName

Specifies the name of the class or method to generate the Java class.

View source code:

@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.FILE)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
public actual annotation class JvmName(actual val name: String)
Copy the code

Depending on the annotation’s declaration, the accessor getter/setter for the property can also use the annotation, but the property cannot use ~.

All functions and attributes declared in the daqikotlin.kt file (including extension functions) are compiled as static methods of a Java class named in DaqiKotlinKt. The first letter of the file name is changed to uppercase, followed by Kt. When you need to change the name of the Java class generated by this Kotlin file, you can specify a specific file name to generate using the @jvmName name:

@file:JvmName("daqiKotlin")

package com.daqi.test

@JvmName("daqiStateFunc")
public fun daqiFunc(){

}
Copy the code

Decomcompiling shows that the generated Java class name has been changed to daqiKotlin instead of DaqiKotlinKt, and the top-level function daqiFunc method name has been changed to daqiStateFunc:

public final class DaqiKotlinKt {
   @JvmName(name = "daqiStateFunc")
   public static final void daqiStateFunc() {}}Copy the code

@JvmMultifileClass

Instructs the Kotlin compiler to generate a multifile class. This file has the top-level functions and attributes declared in this file.

View source code:

@Target(AnnotationTarget.FILE)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
public actual annotation class JvmMultifileClass
Copy the code

When you need to group methods and attributes from multiple Kotlin files into a single Java class, you can declare the same @jVMName in multiple files and add the @JVMMultiFileclass annotation underneath it. (Multiple files declare the same @jvmName, but do not add @jVMMultiFileclass will compile failed)

#daqiKotlin.kt
@file:JvmName("daqiKotlin")
@file:JvmMultifileClass

package com.daqi.test

fun daqi(){

}
Copy the code
#daqiKotlin2.kt
@file:JvmName("daqiKotlin")
@file:JvmMultifileClass

package com.daqi.test

fun daqi2(){

}
Copy the code

The Kotlin compiler merges the methods and attributes from these two files into the named Java class generated by the @jVMName annotation:

@JvmOverloads

Instructs the Kotlin compiler to generate an overloaded function for this function that replaces the default parameter values (omitting each argument starting with the last).

View source code:

@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CONSTRUCTOR)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
public actual annotation class JvmOverloads
Copy the code

There is no concept of default parameter values in Java, and when you call Kotlin’s default parameter function from Java, you must explicitly specify all parameter values. Using the @jvMoverloads annotation to this method, the Kotlin compiler generates the corresponding Java overloaded function, omitting each function starting with the last argument.

#daqiKotlin.kt
@JvmOverloads
fun daqi(name :String = "daqi",age :Int = 2019){
    println("name = $name,age = $age ")}Copy the code

@JvmStatic

An accessor to an object declaration or associated object’s method or property is exposed as a Java static method of the same name.

View source code:

@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
public actual annotation class JvmStatic
Copy the code

For Kotlin’s object declarations and associated objects, the class name can be called in Kotlin as if it were a static function. Method name to call. But in Java, you need to add an extra Companion or INSTANCE to this, making the call unnatural. Use the @jVMStatic annotation to mark up methods and properties in the accompanying object or object declaration so that they can be invoked in Java just like Kotlin.

Define a companion object in Kotlin annotated with the @jVMStatic tag:

class daqi{
    companion object {
        @JvmStatic
        val name:String = ""

        @JvmStatic
        fun daqiFunc() {}}}Copy the code

Decompilation observes that the associated object class or object declaration class declares its own methods and properties, but also declares the same static method and property accessors in the object declaration class itself or in the external class of the associated object class for direct external access.

public final class daqi {
   @NotNull
   private static final String name = "";
   public static final daqi.Companion Companion = new daqi.Companion((DefaultConstructorMarker)null);

   @NotNull
   public static final String getName() {
      daqi.Companion var10000 = Companion;
      return name;
   }

   @JvmStatic
   public static final void daqiFunc() {
      Companion.daqiFunc();
   }

   public static final class Companion {
      @JvmStatic
      public static void name$annotations() {
      }

      @NotNull
      public final String getName() {
         return daqi.name;
      }

      @JvmStatic
      public final void daqiFunc() {}}}Copy the code

Therefore, it is recommended to add @jVMStatic if object declarations and associated objects need to interact with the Java layer more frequently

@ JvmSuppressWildcards and @ JvmWildcard

@jVMSuppresswildcards instructs the compiler to generate or omit wildcards for generic arguments. (Omitted by default.) @jVMwildcard instructs the compiler to generate wildcards for generic parameters.

View source code:

@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, AnnotationTarget.TYPE)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
public actual annotation class JvmSuppressWildcards(actual val suppress: Boolean = true)

--------------------------------------------------------------------------

@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.BINARY)
@MustBeDocumented
public actual annotation class JvmWildcard
Copy the code

@PurelyImplements

Instructs the Kotlin compiler to treat a Java class with the annotation as a pure implementation of the given Kotlin interface. “Pure” here means that each type parameter of the class becomes a non-platform type parameter of the interface.

View source code:

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
public annotation class PurelyImplements(val value: String)
Copy the code

Kotlin treats variables from Java as platform types, leaving it up to the developer to decide whether they are nullable or non-nullable. But even if it is declared non-null, it can still receive or return null values.

# Java file
class MyList<T> extends AbstractList<T> { ... }
Copy the code
# kotlin fileMyList<Int>().add(nullCopy the code

But you can annotate @purelyimplements and carry the corresponding Kotlin interface. The type parameter corresponding to the Kotlin interface is not treated as a platform type.

# Java file
@PurelyImplements("kotlin.collections.MutableList")
class MyPureList<T> extends AbstractList<T> { ... }
Copy the code
MyPureList<Int>().add(null) MyPureList<Int? >().add(null) // The compiler passesCopy the code

@Throws

This is equivalent to the Throws keyword of Java

View source code:

@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.CONSTRUCTOR)
@Retention(AnnotationRetention.SOURCE)
public annotation class Throws(vararg val exceptionClasses: KClass<out Throwable>)
Copy the code

example

@Throws(IOException::class)
fun daqi() {}Copy the code

@Strictfp

Equivalent to the Java strictFP keyword

View source code:

@Target(FUNCTION, CONSTRUCTOR, PROPERTY_GETTER, PROPERTY_SETTER, CLASS)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
public actual annotation class Strictfp
Copy the code

@Transient

Equivalent to the Java TRANSIENT keyword

View source code:

@Target(FIELD)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
public actual annotation class Transient
Copy the code

@Synchronized

Equivalent to the Java synchronized keyword

View source code:

@Target(FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
public actual annotation class Synchronized
Copy the code

@Volatile

Equivalent to the Java volatile keyword

View source code:

@Target(FIELD)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
public actual annotation class Volatile
Copy the code

Point target statement

In many cases, a single declaration in Kotlin code corresponds to multiple Java declarations, and each of them can carry annotations. For example, the Kotlin property corresponds to Java segments, getters, and a potential setter. You need to use point targets to specify where the annotation should be used.

The dot target declaration is used to specify the element to annotate. The use dot target is placed between the @ symbol and the annotation name, separated by a colon from the annotation name.

The full list of point targets is as follows:

  • Property ————Java annotations cannot apply this use point target
  • Field ———— Fields generated for the property
  • Get ———— The getter for the property
  • Setters for the set ———— property
  • Receiver ———— Receiver parameters of an extension function or extension property.
  • Param ———— constructor argument.
  • Setparam ———— arguments to the setter property
  • Delegate ———— stores the fields of the delegate instance for the delegate property
  • File ———— contains the classes for top-level functions and attributes declared in the file.
@get:daqiAnnotation val daqi:String =""

@Target(AnnotationTarget.PROPERTY_GETTER)
annotation class daqiAnnotation()
Copy the code

References:

  • Kotlin in Action
  • Kotlin website

Android Kotlin series:

Kotlin’s Knowledge generalization (I) — Basic Grammar

Kotlin knowledge generalization (2) – make functions easier to call

Kotlin’s knowledge generalization (iii) — Top-level members and extensions

Kotlin knowledge generalization (4) – interfaces and classes

Kotlin’s knowledge induction (v) — Lambda

Kotlin’s knowledge generalization (vi) — Type system

Kotlin’s knowledge induction (7) — set

Kotlin’s knowledge induction (viii) — sequence

Kotlin knowledge induction (ix) — Convention

Kotlin’s knowledge induction (10) — Delegation

Kotlin’s knowledge generalization (xi) — Higher order functions

Kotlin’s generalization of knowledge (xii) – generics

Kotlin’s Generalization of Knowledge (XIII) — Notes

Kotlin’s Knowledge induction (xiv) — Reflection