The craftsman may be late, but he is never absent. Finally, he decided to share his cloud notes

In particular, the Kotlin series of articles are distilled to focus on Java differences, and the same parts as Java are not listed. With the iteration of kotlin’s official version, some of the syntax in this article may change, please be aware that the essence of language can be understood, the difference is only at the grammar level, it is recommended not to pay too much attention to the grammar.

The definition of a class

Kotlin’s class definition is much simpler than Java’s, with general classes defined as follows (special classes are summarized in the following section) :

// The kotlin class defaults to public, and does not specify public explicitly
class MyClass {}// In kotlin, the curly braces can be omitted if the body of a class has no content, as follows:
class MyClass
Copy the code

Unlike Java, kotlin does not require the new keyword to instantiate a class.

Constructor and initialization process

In Kotlin, a class can have a primary constructor and one or more secondary constructors.

// The primary constructor is part of the class header, followed by the class name, and can take several arguments. The default modifier is public
class TestClass constructor(name: String, age: Int) {
    private val name = name.toLowerCase()
    // The constructor argument can be used directly in the initialization code block to assign initial values to class object attributes
    init {
        println(age)
    }
}

// Private constructor class definition
class TestClass1 private constructor(name: String)

// If the primary constructor does not have any annotations or visibility keywords, the constructor keyword can be omitted
class TestClass2 (name: String)
Copy the code

Example of how to write multiple constructor declarations (including code execution order) :

/ / 【 craftsman if water to add WeChat yanbo373131686 contact me, concern WeChat public number: code farmers a daily topic Without permission is strictly prohibited reproduced, https://blog.csdn.net/yanbober]

class Person constructor(name: String) {
    private var name: String
    private var age: Int
    private var address: String

    init {
        println(name)   // Name is an argument to the constructor method
        this.name = name
        this.age = 16
        this.address = "zhuhai"
    }
    // The secondary constructor must call its primary constructor directly or indirectly
    constructor(name: String, age: Int) :this(name) {
        println("$name.$age")
        this.name = name
        this.age = age
        this.address = "hanzhong"
    }
    // The secondary constructor calls its primary constructor indirectly
    constructor(name: String, age: Int, address: String): this(name, age) {
        this.address = address
    }

    fun print(a) {
        println("name: ${this.name}, age: $age, address: $address")}}/** Run the following output value: ruoshui gongjiang gongjiang, 18 android android, 19 name: ruoshui, age: 16, address: zhuhai name: zhuhai name: gongjiang, age: 18, address: hanzhong name: android, age: 19, address: guangdong */
fun testRun(a) {
    var person0 = Person("ruoshui")
    var person1 = Person("gongjiang".18)
    var person2 = Person("android".19."guangdong")
    person0.print()
    person1.print()
    person2.print()
}
Copy the code

Kotlin class syntax sugar shorthand implementation of the above case:

// Short: A direct constructor declares a class's member variable attributes
class Person1 (private val name: String,
                private var age: Int.private val address: String) {
    fun print(a) {
        println("name: ${this.name}, age: $age, address: $address")}}/** The output values are: name: yan, age: 18, address: guangdong */
fun testRun(a) {
    var person = Person1("yan".18."guangdong")
    person.print()
}
Copy the code

Unlike Java, Kotlin’s constructor can have default parameter values. If all parameters of the primary constructor have default values, the compiler also generates an additional new no-argument constructor and uses the default values of this constructor. Specific usage is similar to the following:

// If all the parameters of the primary constructor have default values, the compiler also generates an additional new no-argument constructor and uses the default values of this constructor
class Person2 (private val name: String = "ruoshui".private var age: Int = 20.private var address: String = "zhuhai") {
    fun print(a) {
        println("name: ${this.name}, age: $age, address: $address")}}class Person3 (private val name: String = "ruoshui".private var age: Int) {
    fun print(a) {
        println("name: ${this.name}, age: $age")}}/**
 运行输出值为:
 name: ruoshui, age: 20, address: zhuhai
 name: ruoshui, age: 12
 */
fun testRun(a) {
    var person = Person2()
    person.print()
    // The primary constructor argument has an incomplete default value, so no-parameter construction cannot be used
    var person1 = Person3(age = 12)
    person1.print()
}
Copy the code

Inheritance and override features

Another difference from the Java class default modifier is that all classes in Kotlin are not inherited by default, that is, all classes in Kotlin are final by default. The following example demonstrates class inheritance:

/ / 【 craftsman if water to add WeChat yanbo373131686 contact me, concern WeChat public number: code farmers a daily topic Without permission is strictly prohibited reproduced, https://blog.csdn.net/yanbober]

// The default class is final and you need to explicitly add open so that it can be inherited
open class Anim (name: String, age: Int)

// Kotlin passes: Implements Java extends and other keywords
class Dog (name: String, age: Int): Anim(name, age)

// If a class in Kotlin does not have a primary constructor,
// Each secondary constructor of this class needs to initialize the parent type with the super keyword,
// Do this via another secondary constructor.
// Different secondary constructors can call constructors of different parent types.
open class Anim1 (name: String) {
    init {
        println("Anim1--$name")}}class Dog1 : Anim1 {
    constructor(name: String): super(name) {
        println("dog1-0--$name")}constructor(name: String, age: Int) :this(name) {
        println("dog1-2--$name")}}Anim1--666 dog1-0--666 dog1-2--666 */
fun testRun(a) {
    var anim = Dog1("666".66)}Copy the code

Then we look at the rewriting of class methods in Kotlin, which is also quite different from Java. If a method in Kotlin wants to be overridden, it must explicitly specify the open keyword, otherwise it will not compile. Override methods in subclasses must display the override keyword or fail to compile. Examples are as follows:

open class Developer {
    If a method in Kotlin wants to be overridden, it must explicitly specify the open keyword, otherwise it will not compile
    open fun skill(a) {
        println("developer")}// Subclasses have this capability by default, but cannot be overridden
    fun money(a) {
        println("666")}// Can be inherited by subclasses
    open fun wife(a) {
        println("haha")}}open class Android: Developer() {
    The override method in kotlin must display the override keyword, or it will fail to compile
    override fun skill(a) {
        println("android")}// The Wife method of the Android class overrides the parent class, but cannot be overridden by subclasses because final is declared
    final override fun wife(a) {
        super.wife()
        println("green")}}/** The output value is android 666 haha green */
fun testRun(a) {
    var dev = Android()
    dev.skill()
    dev.money()
    dev.wife()
}
Copy the code

After class method overrides, let’s look at Kotlin’s class attribute overrides. The property of val can be overridden as var, and the property of var cannot be overridden as val. Since val is a get method and var is a GET and set method, it is possible to increase the scope, but not to reduce it. Here are the use cases:

open class PaPaPa {
    open val name: String = "pa"

    open fun print(a) {
        println("name=$name, this.name=${this.name}")}}open class XoXoXo: PaPaPa() {
    override val name: String = "xo"
    The val read-only attribute has only get methods by default
    open val address: String get() = "beijing"
}

// Attribute rewrite shorthand
class NcNcNc (override var name: String = "nc"): XoXoXo() {
    override fun print(a) {
        super.print()
        println("address is $address")}// Override the parent property and call the parent property
    override val address: String
        get() = "6666" + super.address
}

Name =nc, this.name=nc address is 6666Beijing */
fun testRun(a) {
    var oo = NcNcNc()
    oo.print()
}
Copy the code

As with Java, there is a special class inheritance, that is the interface, kotlin interface implementation is similar to Java, as shown in the following example:

/ / 【 craftsman if water to add WeChat yanbo373131686 contact me, concern WeChat public number: code farmers a daily topic Without permission is strictly prohibited reproduced, https://blog.csdn.net/yanbober]

interface Nb {
    // Interface declaration
    fun test1(a)
    // Interfaces can have both declarations and implementations
    fun test(a) {
        println("nb")}}class ChildNb: Nb {
    override fun test1(a) {
        println("test1")}override fun test(a) {
        println("test")
        super.test()
    }
}
Copy the code

Kotlin provides a solution to mixed-use conflicts. When the inherited class and interface have the same signature method, the subclass must override the same signature method of the parent class, otherwise it will not compile, and it needs to explicitly specify which base class method to use through the <> syntax. Examples are as follows:

interface Nb {
    fun test(a) {
        println("nb")}}open class Tt {
    open fun test(a) {
        println("tt")}}class ChildNb: Nb.Tt() {
    // Subclasses must override the same signature method of their parent class if the inherited class and interface have the same signature method
    // You also need to explicitly specify which base class method to use with <>
    override fun test(a) {
        super<Tt>.test()
        println("test")
        super<Nb>.test()
    }
}

/** The output value is tt test nb */
fun testRun(a) {
    var oo = ChildNb()
    oo.test()
}
Copy the code

The declaration and inheritance of abstract classes are basically the same as in Java, as follows:

open class Base {
    open fun method(a) {
        println("base")}}abstract class Child: Base() {
    // The implementation methods of the parent class can be overwritten by the abstract subclass as abstract methods for implementation by the current abstract subclass
    override abstract fun method(a)
}

class Child1: Child() {
    override fun method(a) {
        println("child1")}}Copy the code

Object declaration and associated objects

Kotlin’s object declarations support implementation capabilities at the language level as a singleton. Define a class with class and declare an object with object. A globally declared object is an instance of an object and is globally unique. Examples are as follows:

// Defines an object instance named TestObject
object TestObject {
    fun method(a) {
        println("666")}}/** Run the output value: 666 */
fun testRun(a) {
    TestObject.method()
}
Copy the code

Kotlin’s companion objects are also new to Java, and kotlin differs from Java essentially in that kotlin classes have no static methods. In most cases, Kotlin recommends using package-level functions to act as static methods. Kotlin treats package-level functions as static methods. In Kotlin, a class can have at most one companion object, which is also like a Java static member. Examples are as follows:

/ / 【 craftsman if water to add WeChat yanbo373131686 contact me, concern WeChat public number: code farmers a daily topic Without permission is strictly prohibited reproduced, https://blog.csdn.net/yanbober]

class Yan {
    // The TestObj name can be omitted and the compiler default name is Companion
    // A class can have at most one associated object
    companion object TestObj {
        var name: String = "666"
        fun method(a) = println("${this.name} -- method")}}Yan$TestObj */ 666 -- method 777 -- method class cn.yan.test. yan $TestObj */
fun testRun(a) {
    Yan.TestObj.method()
    // Kotlin syntax candy, without @jvmstatic is essentially changed to yann.testobj static member call method
    Yan.name = "777"
    Yan.method()
    println(Yan.TestObj.javaClass)
}
Copy the code

While the members of the companion object look like Java static members, at run time they are still instance members of the real object. JVM implementations can actually generate the members of an associated object as static methods and properties of a class, using the @jVMStatic annotation.

The essence of the associated object is to generate a static inner class after compilation. We decompiled the above example code (javAP) and the result is as follows:

// Decompilated cn.yan.test. yan class
yandeMacBook-Pro:test yan$ javap -c Yan.class
Compiled from "Test2.kt"
public final class cn.yan.test.Yan {
  //Yan the associated object name in the Kotlin class generates a static member variable named TestObj
  public static final cn.yan.test.Yan$TestObj TestObj;
  //Yan is the kotlin constructor
  public cn.yan.test.Yan();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return
  //Yan the static block of the Kotlin class instantiates the Yan$TestObj static inner class and assigns it to the static member property TestObj of the current class
  static {};
    Code:
       0: new           #38                 // class cn/yan/test/Yan$TestObj
       3: dup
       4: aconst_null
       5: invokespecial #41                 // Method cn/yan/test/Yan$TestObj."
      
       ":(Lkotlin/jvm/internal/DefaultConstructorMarker;) V
      
       8: putstatic     #43                 // Field TestObj:Lcn/yan/test/Yan$TestObj;
      11: ldc           #45                 // String 666
      13: putstatic     #20                 // Field name:Ljava/lang/String;
      16: return
  // Add a get operation for the attribute var name in the Yan class
  public static final java.lang.String access$getName$cp();
    Code:
       0: getstatic     #20                 // Field name:Ljava/lang/String;
       3: areturn
  // Add a set operation for the attribute var name in the Yan class
  public static final void access$setName$cp(java.lang.String);
    Code:
       0: aload_0
       1: putstatic     #20                 // Field name:Ljava/lang/String;
       4: return
}
Copy the code

The static inner class generated by the associated object of the Kotlin class Yan is decompiled below:

Yan$TestObj; // static inner class yan $TestObj generated by the associated object in the decompilated cn.yan.test. yan class
yandeMacBook-Pro:test yan$ javap -c Yan\$TestObj.class
Compiled from "Test2.kt"
public final class cn.yan.test.Yan$TestObj {
  // The get method associated with the object's internal attribute var name
  public final java.lang.String getName(a);
    Code:
       0: invokestatic  #12                 // Method cn/yan/test/Yan.access$getName$cp:()Ljava/lang/String;
       3: areturn
  // The set method associated with the object's internal attribute var name
  public final void setName(java.lang.String);
    Code:
       0: aload_1
       1: ldc           #18                 // String 
      ?>
       3: invokestatic  #24                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object; Ljava/lang/String;) V
       6: aload_1
       7: invokestatic  #27                 // Method cn/yan/test/Yan.access$setName$cp:(Ljava/lang/String;) V
      10: return
  // The associated object internally defined method
  public final void method(a);
    Code:
       0: new           #32                 // class java/lang/StringBuilder
       3: dup
       4: invokespecial #35                 // Method java/lang/StringBuilder."<init>":()V
       7: aload_0
       8: checkcast     #2                  // class cn/yan/test/Yan$TestObj
      11: invokevirtual #37                 // Method getName:()Ljava/lang/String;
      14: invokevirtual #41                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;) Ljava/lang/StringBuilder;
      17: ldc           #43                 // String -- method
      19: invokevirtual #41                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;) Ljava/lang/StringBuilder;
      22: invokevirtual #46                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      25: astore_1
      26: iconst_0
      27: istore_2
      28: getstatic     #52                 // Field java/lang/System.out:Ljava/io/PrintStream;
      31: aload_1
      32: invokevirtual #58                 // Method java/io/PrintStream.println:(Ljava/lang/Object;) V
      35: return
  // the constructor for the static inner class generated by the associated object
  public cn.yan.test.Yan$TestObj(kotlin.jvm.internal.DefaultConstructorMarker);
    Code:
       0: aload_0
       1: invokespecial #61                 // Method "<init>":()V
       4: return
}
Copy the code

We then add the @jVMStatic annotation to the kotlin case code above, as follows:

class Yan {
    companion object TestObj {
        @JvmStatic
        var name: String = "666"
        @JvmStatic
        fun method(a) = println("${this.name} -- method")}}Copy the code

The decompilation result of the above code is as follows:

/ / 【 craftsman if water to add WeChat yanbo373131686 contact me, concern WeChat public number: code farmers a daily topic Without permission is strictly prohibited reproduced, https://blog.csdn.net/yanbober]

yandeMacBook-Pro:test yan$ javap -c Yan.class
Compiled from "Test2.kt"
public final class cn.yan.test.Yan {
  // Static attributes similar to those without annotations above
  public static final cn.yan.test.Yan$TestObj TestObj;
  // Similar to no annotation above, constructor
  public cn.yan.test.Yan();
    Code:
       ......
  // Static code blocks and assigns values to static members, similar to the above
  static {};
    Code:
       ......
  // As not commented above, the internal directive access method
  public static final java.lang.String access$getName$cp();
    Code:
       ......
  // As not commented above, the internal directive access method
  public static final void access$setName$cp(java.lang.String);
    Code:
       ......
  // Add @jvmstatic and add the static property get method to the Yan class that the associated object depends on
  public static final java.lang.String getName(a);
    Code:
       0: getstatic     #39                 // Field TestObj:Lcn/yan/test/Yan$TestObj;
       3: getstatic     #20                 // Field name:Ljava/lang/String;
       6: areturn
  // Add @jvmStatic to the Yan class that the associated object depends on
  public static final void setName(java.lang.String);
    Code:
       0: getstatic     #39                 // Field TestObj:Lcn/yan/test/Yan$TestObj;
       3: aload_0
       4: putstatic     #20                 // Field name:Ljava/lang/String;
       7: return
  // Call to the static method added in the Yan class that the associated object depends on after adding @jvmStatic
  public static final void method(a);
    Code:
       0: getstatic     #39                 // Field TestObj:Lcn/yan/test/Yan$TestObj;
       3: invokevirtual #46                 // Method cn/yan/test/Yan$TestObj.method:()V
       6: return
}
Copy the code

The static inner class generated by the associated object of the Kotlin class Yan is decompiled below:

yandeMacBook-Pro:test yan$ javap -c Yan\$TestObj.class
Compiled from "Test2.kt"
public final class cn.yan.test.Yan$TestObj {
  // It is basically the same as above without annotations, so it will not be listed. }Copy the code

As you can see from the above example, the @jVMStatic annotation does not make any difference to the user, only to the bytecode generated after compilation. The difference is analyzed above, but it is important to remember that the nature of the associated object is implemented through static inner classes and static members.

Object expressions differ from object declarations and associated objects:

  • Object expressions are initialized or executed immediately.
  • Object declarations are lazily initialized at first access.
  • An associated object is initialized when its corresponding class is loaded, corresponding to Java’s static initialization.

Property and lazy initialization

Class member attributes in Kotlin are defined similarly to Those in Java, as shown in the following example:

class Do(name: String, address: String) {
    // Define read-only attributes
    val name: String
        get() = "gongjiang"
    // Define readable and writable attributes
    var address: String = address
        get() {
            println("get address")
            return field
        }
        set(value) {
            println("set address")
            // we can't use this.address = value
            field = value
        }
}

Gongjiang set address get address hanzhong */
fun testRun(a) {
    val dx = Do("yb"."zhuhai")
    // Get method of the name attribute
    println(dx.name)
    dx.address = "hanzhong"
    println(dx.address)
}
Copy the code

We decompiled the Do class as follows:

yandeMacBook-Pro:test yan$ javap Do.class
Compiled from "Test2.kt"
public final class cn.yan.test.Do {
  // The read-only property name defined by val only generates the public get method
  public final java.lang.String getName(a);
  // The address attribute defined by val generates the public get and set methods
  public final java.lang.String getAddress(a);
  public final void setAddress(java.lang.String);
  // the constructor is generated
  public cn.yan.test.Do(java.lang.String, java.lang.String);
}
Copy the code

Kotlin requires that properties of non-nullable types be initialized in the constructor. This is sometimes inconvenient, such as for dependency injection (Spring, Room, etc.), where the lateInit keyword is used to indicate that the property is lazily initialized.

  • Lateinit can only be used on var properties declared in the class body, not on properties declared in the primary constructor.
  • Properties cannot have custom get and set methods.
  • The attribute type must be non-empty and cannot be a primitive type (such as Int).

Here is an example of lazy initialization of an attribute:

class Qq {
    // Lazy initialization, do not add must be initialized or declared as String? type
    lateinit var name: String
    // Failed to compile: Property must be initialized or be abstract
    //var age: Int

    // Failed to compile: 'lateinit' modifier is allowed only on mutable properties
    //lateinit val address: String

    'Lateinit' modifier is not allowed on properties of primitive types
    //lateinit var count: Int

    fun print(a) {
        println(name)
    }
}

/ * * run results directly collapse: the Exception in the thread "is the main" kotlin. UninitializedPropertyAccessException: lateinit property name has not been initialized at cn.yan.test.Qq.print(Test2.kt:324) at cn.yan.test.Test2Kt.testRun(Test2.kt:337) at cn.yan.test.Test2Kt.main(Test2.kt:22) at cn.yan.test.Test2Kt.main(Test2.kt) * /
fun testRun(a) {
    val qq = Qq()
    qq.print()
}
Copy the code

Visibility and Kotlin extension

Kotlin provides four visibility modifiers: private, protected, internal, and public. The default for kotlin visibility modifier is public.

The top non-class method or property in a Kotlin file or class that adds the private modifier uses scope to the current file.

Internal means that you can only use the same module. Java has no module, and Kotlin’s module is a module that is compiled together at the last compile time.

The protected modifier cannot be used in top-level classes or functions or attributes. Can be used in a non-top-level declaration that ranges from a subclass to the current class.

Local variables are concepts without access modifiers.

Kotlin’s extensions are an incredible concept compared to Java. If we have defined a class and now want to add additional functionality to that class, the only way to solve this problem in Java is to inherit or decorator implementation, whereas kotlin’s extensions can easily do this. Here is an extended example:

/ / Test2. Kt file
// Assume that the class is not self-written or exists in a JAR
class Xunit {
    var count: Int = 0

    fun add(a: Int, b: Int) = a + b
    fun sub(a: Int, b: Int) = a - b

    fun printCount(a) = println("count=$count")}// The Kotlin extension adds methods to Xunit. In essence, multi methods are not compiled and inserted into the Xunit class
fun Xunit.multi(a: Int, b: Int) = a * b
fun Xunit.doCount(a: Int) {
    this.count = a
}

/** Count =12 */
fun testRun(a) {
    val xunit = Xunit()
    println(xunit.add(1.1))
    println(xunit.sub(2.1))
    // Call the extension function of the class, which is essentially a syntax candy
    println(xunit.multi(2.2))
    xunit.doCount(12)
    xunit.printCount()
}
Copy the code

In order to see how the extension works, we need to be clear that the extension itself does not actually modify the target class. Methods are not compiled and inserted into the Xunit class, but in the class defined by it. See the decompiled bytecode in the example above:

/ / 【 craftsman if water to add WeChat yanbo373131686 contact me, concern WeChat public number: code farmers a daily topic Without permission is strictly prohibited reproduced, https://blog.csdn.net/yanbober]

yandeMacBook-Pro:test yan$ javap Xunit.class
Compiled from "Test2.kt"
// The Xunit class has no properties and methods we extended
public final class cn.yan.test.Xunit {
  public final int getCount(a);
  public final void setCount(int);
  public final int add(int.int);
  public final int sub(int.int);
  public final void printCount(a);
  public cn.yan.test.Xunit();
}
Copy the code

Extension method code decompilation implementation:

Compiled from "Test2.kt"
// The multi and doCount extension methods are here and have an additional first parameter of type Xunit, so it is essentially a wrapper implementation
public final class Test2Kt {
  // The two extension methods are static
  public static final int multi(Xunit, int.int);
  public static final void doCount(Xunit, int);
  public static final void testRun(a);
  public static final void main(a);
  public static void main(java.lang.String[]);
}
Copy the code

Decompilation shows that the extension itself does not actually modify the target class, but is compiled into the defined class. In addition, you can find that the resolution of the extension function is statically distributed, not dynamic, does not support polymorphism, and calls only depend on the declared type of the object, not the actual type, as follows:

open class Parent
class Children: Parent(a)fun Parent.method(a) = "parent"
fun Children.method(a) = "children"

/** Result: parent children parent */
fun testRun(a) {
    val tmp1 = Parent()
    println(tmp1.method())
    val tmp2 = Children()
    println(tmp2.method())
    val tmp3: Parent = Children()
    println(tmp3.method())
}
Copy the code

Let’s look at two examples of whether an extension method can be inherited:

open class Parent
class Children: Parent(a)fun Parent.method(a) = "parent"

/** Result: parent parent parent */
fun testRun(a) {
    val tmp1 = Parent()
    println(tmp1.method())
    val tmp2 = Children()
    println(tmp2.method())
    val tmp3: Parent = Children()
    println(tmp3.method())
}
Copy the code
open class Parent
class Children: Parent(a)fun Children.method(a) = "children"

/** Compiling error */
fun testRun(a) {
    val tmp1 = Parent()
    //println(tmp1.method()) compiler error: method cannot be found
    val tmp2 = Children()
    println(tmp2.method())
    val tmp3: Parent = Children()
    //println(tmp3.method()) compiler error: method cannot be found
}
Copy the code

When the signature of an extension function overlains the signature of an existing function of the extended class (try not to do this), the existing method of the class takes precedence. Examples are as follows:

fun Sss.method(a) {
    println("sss method 2")}fun Sss.method(str: String) {
    println("sss method 2 $str")}SSS method 1 SSS method 2 666 */
fun testRun(a) {
    val ss = Sss()
    // If a method in a class has exactly the same signature as its extension method, the class's original method takes precedence
    ss.method()
    // Extension supports method overloading
    ss.method("666")}Copy the code

In addition, Kotlin can extend nullable types as follows:

/ / 【 craftsman if water to add WeChat yanbo373131686 contact me, concern WeChat public number: code farmers a daily topic Without permission is strictly prohibited reproduced, https://blog.csdn.net/yanbober]

funAny? .toString(a): String {
    if (this= =null) {
        return "null---"
    }
    return toString()
}

/** Run result: null-- 1 */
fun testRun(a) {
    var tmp: Int? = null
    println(tmp.toString())
    tmp = 1
    println(tmp.toString())
}
Copy the code

Except for extension functions, Kotlin’s extension properties are basically similar to extension functions. Examples are as follows:

class Fff
val Fff.name: String get() = "gongjiang"

Gongjiang */
fun testRun(a) {
    val ff = Fff()
    println(ff.name)
}
Copy the code

We can also extend the associated objects. The following cases:

class Fff {
    companion object {
        val name: String = "999"

        fun go(a) {
            println("go--$name")}}}fun Fff.Companion.method(a) {
    println("method--${this.name}")}/** Method --999 go--999 */
fun testRun(a) {
    val ff = Fff()
    //ff.method() compiler error, object does not have this method
    //ff.go() compiler error, object does not have this method
    Fff.method()
    Fff.go()
}
Copy the code

Extensions are also scoped. The above extension cases are defined in kotlin’s top-level file, but extensions can also be defined in other classes. The instance of the class in which the extension function is defined is called a Dispatch receiver, and the instance of the class extended by the extension function is called an extension receiver. When the two names conflict, the extension receiver takes precedence. Case demonstration is as follows:

class HH {
    fun method(a) = println("HH method")}class KK {
    fun method2(a) = println("KK method2")

    fun HH.inn(a) {
        // You can directly use members that the HH class already has
        method()
        // You can also use the member of the class that defines the extension function
        method2()
    }

    fun go(hh: HH) {
        // The extension member of HH can be called directly in KK, because the extension is defined in KK
        hh.inn()
    }

    fun HH.out(a) {
        // both HH and KK have toString() methods, so the actual call to HH has a higher priority
        println(toString())
        println(this.toString())
        // Specify that the toString() method of KK is called
        println(this@KK.toString())
    }

    fun test(a) {
        val hh = HH()
        hh.out()}}/** Result: cn.yan.test.HH@19469ea2 cn.yan.test.HH@19469ea2 cn.yan.test.KK@13221655 */
fun testRun(a) {
    val hh = HH()
    Error:(Unresolved, 8) Kotlin: Unresolved reference: out
    //hh.out() // can only be used in the declaration scope because it is not defined at the top level
    val kk = KK()
    kk.test()
}
Copy the code

As we can see from the above series of extension cases, Kotlin is a good solution to Java’s extension helper classes (various Utils wrapper definitions).

Kotlin data classes and deconstruction

Kotlin’s data classes are essentially Java Bean entity classes, but kotlin’s data class definition has its own special syntax and is much more convenient. Examples are as follows:

/ / data
data class Data(var name: String, var age: Int)
/ / ordinary class
class Data1(var name: String, var age: Int)

/** Result: Data(name=name, age=12) cn.yan.test.Data1@19469ea2 */
fun testRun(a) {
    val data = Data("name".12)
    println(data)
    val data1 = Data1("name1".13)
    println(data1)
}
Copy the code

To define a data class in Kotlin, you need to:

  • The main constructor must have at least one parameter.
  • All main constructor arguments need to be marked as var or val.
  • Data classes cannot be abstract, open, sealed, or inner.

For data classes the Kotlin compiler does the following extra things for us:

  • The equals and hashCode methods are automatically overridden.
  • Automatic fixed formatting overrides the toString method.
  • Automatically generates componentN methods for properties in the order in which they are declared.

The decompilation of the Kotlin data class is as follows:

/ / 【 craftsman if water to add WeChat yanbo373131686 contact me, concern WeChat public number: code farmers a daily topic Without permission is strictly prohibited reproduced, https://blog.csdn.net/yanbober]

yandeMacBook-Pro:test yan$ javap Data.class
Compiled from "Test2.kt"
// You can see that the data class Kotlin generates a lot of extra methods for us
public final class cn.yan.test.Data {
  public final java.lang.String getName(a);
  public final void setName(java.lang.String);
  public final int getAge(a);
  public final void setAge(int);
  public cn.yan.test.Data(java.lang.String, int);
  public final java.lang.String component1(a);
  public final int component2(a);
  public final cn.yan.test.Data copy(java.lang.String, int);
  public static cn.yan.test.Data copy$default(cn.yan.test.Data, java.lang.String, int.int, java.lang.Object);
  public java.lang.String toString(a);
  public int hashCode(a);
  public boolean equals(java.lang.Object);
}
Copy the code

The Kotlin non-data class is decompilated as follows:

yandeMacBook-Pro:test yan$ javap Data1.class
Compiled from "Test2.kt"
// You can see that the non-data class Kotlin only generates for us some of the attribute methods and methods we declare
public final class cn.yan.test.Data1 {
  public final java.lang.String getName(a);
  public final void setName(java.lang.String);
  public final int getAge(a);
  public final void setAge(int);
  public cn.yan.test.Data1(java.lang.String, int);
}
Copy the code

There are a few additional points to note about the inheritance of Kotlin’s data class members:

  • If the equals, hashCode, and toString methods are explicitly defined in the data class, or declared final in the parent of the data class, these methods will no longer be generated automatically and will be used instead.
  • If a parent class has componentN methods that are open and return compatible types, the compiler generates corresponding componentN methods in the data class and overrides the parent class’s methods. If these methods of the parent class are defined as final due to incompatible signatures, the compiler will report an error.
  • Explicit provision of componentN methods and copy method implementations in data classes is not allowed.

Here is a use case for the data class:

data class Data(var name: String, var age: Int)

/** Result: Data(name=name, age=12) Data(name=name, age=12) Data(name=name, age=30) a=name, b=30 */
fun testRun(a) {
    val data = Data("name".12)
    println(data)
    // Copy and modify one of the property values
    val data1 = data.copy(age = 30) // A special method copy that is automatically generated by the data class
    println(data)
    println(data1)
    val (a, b) = data1 // Destruct the assignment, using the componentN feature
    println "a=$a, b=$b"
}
Copy the code

Kotlin’s data classes can be destructively declared, generating corresponding componentN methods as many parameters as there are in the main constructor of kotlin data classes, which return the values of the corresponding sequential fields. The componentN method is used to deconstruct declarations.

However, there is a problem that we should pay attention to. Since deconstruction requires sequence, please try to append attributes at the end of Kotlin’s data class attribute iteration. Do not insert new attributes in front or in the middle, otherwise it is easy to disrupt the order and lead to the error of the deconstruction caller.

Here is a deconstructed assignment example:

data class Data(var name: String, var age: Int)
class Data1(var name: String, var age: Int)

/** Compile run */
fun testRun(a) {
    val data = Data("name".12)
    val data1 = Data1("name1".13)
    val (na, ag) = data
    println("name=$na, age=$ag")    //name=name, age=12
    // Non-data classes do not generate componentN methods by default
Kotlin: Destructuring declaration initializer of type Data1 must have a 'Component1 ()' function
// val (na1, ag1) = data1
// println("name1=$na1, age1=$ag1")
}
Copy the code

Sealed class (Scaled Class)

Kotlin’s sealed classes are similar to Java’s enumeration classes, but unlike Kotlin, which has its own enumeration classes, sealed classes are new to Java. The sealed class describes a restricted hierarchy of relationships, parent-child relationships. A sealed class can have subclasses, and subclasses of a sealed class can create multiple instances. Direct subclasses of the sealed class must be defined in the same file as the sealed class. indirect subclasses do not have this restriction. Sealed classes are abstract and are not allowed to be instantiated. Sealed classes are not allowed to have private constructors.

Sealed classes are most commonly used in WHEN expressions. The default WHEN expression must provide an elase branch, but with sealed classes (because there are explicitly only two subclasses) you don’t need to provide an else. The use cases are as follows:

/ / 【 craftsman if water to add WeChat yanbo373131686 contact me, concern WeChat public number: code farmers a daily topic Without permission is strictly prohibited reproduced, https://blog.csdn.net/yanbober]

sealed class Option
class AddOption: Option(a)class SubOption: Option(a)fun option(num1: Int, num2: Int, opt: Option) = when (opt) {
    is AddOption -> num1 + num2
    is SubOption -> num1 - num2
    // Omit else because 'when' is exhaustive so 'else' is redundant here
    //else -> num1 * num2
}

/** Run result: 3-1 */
fun testRun(a) {
    println(option(1.2, AddOption()))
    println(option(1.2, SubOption()))
}
Copy the code

Here is a case where the case sealing class does not have a group lift complete scenario in the WHEN expression:

sealed class Option
class AddOption: Option(a)class SubOption: Option(a)class MaxOption: Option(a)// Failed to compile, 'When' expression must be exhaustive, add necessary 'is MaxOption' branch or 'else' branch instead
fun option(num1: Int, num2: Int, opt: Option) = when (opt) {
    is AddOption -> num1 + num2
    is SubOption -> num1 - num2
}
Copy the code

Nested and inner classes

Kotlin’s nested classes correspond to Java’s static inner classes. Java has the static keyword, but Kotlin does not. Defining a class inside a class is Kotlin’s nested class.

Kotlin’s inner classes correspond to non-static inner classes in Java. Classes that are decorated with the inner keyword and defined inside a class are Kotlin’s inner classes.

Modifiers can be added to both inner and outer classes, such as private, etc. Here is a nested and inner class use case demonstration:

class OutClass {
    private val name: String = "out name"

    // Define nested classes
    class NestedClass {
        fun method(a) = "nested method"}}class OutClass2 {
    private val name: String = "out name"

    Inner keyword must be added to define inner class
    inner class InnerClass {
        // The inner class can access the private attributes of the outer class. If the inner class has its own name attribute, it can access its own member attributes
        fun method(a) = "inner method ${[email protected]}"}}Nested method inner method out name */
fun testRun(a) {
    // Instantiation and invocation of nested classes
    // Access to nested classes does not require an external class instance, much like Java's static inner classes
    println(OutClass.NestedClass().method())

    // Instantiation and invocation of inner classes
    // The inner class can be accessed only if there is an instance of the outer class, which is OutClass2()
    println(OutClass2().InnerClass().method())
}
Copy the code

Kotlin also has locally nested classes, but no local inner classes, as follows:

class OutClass {
    private val name: String = "out name"

    fun get(a): String {
        // partially nested classes
        class LocalNestedClass {
            val name = "666"
        }
        return LocalNestedClass().name / / 666
    }

// No local inner class
// fun get1(): String {
// // mysql Modifier 'inner' is not applicable to 'local class'
// inner class LocalInnerClass {
// val test = "666"
/ /}
// return LocalInnerClass().test
/ /}
}
Copy the code

Kotlin object expressions and object declarations

Anonymous inner classes in Java are useful and widely used in many scenarios, and Kotlin’s object expressions are instances of anonymous inner classes. Java anonymous inner classes need to meet the following criteria:

  • Anonymous inner classes have no name;
  • An anonymous inner class must inherit from a parent class or implement an interface;
  • The Java runtime treats this anonymous inner class as the interface it implements or as an inherited parent class;

Similarly, Kotlin’s object expressions satisfy conditions similar to Those in Java, so the syntax is as follows:

object[: several parent types separated by,] {}Copy the code

Here are the use cases for various Kotlin object expressions:

/ / 【 craftsman if water to add WeChat yanbo373131686 contact me, concern WeChat public number: code farmers a daily topic Without permission is strictly prohibited reproduced, https://blog.csdn.net/yanbober]

interface InterfaceBase {
    fun print(i: Int)
}

abstract class AbsInterfaceBase {
    abstract val age: Int

    abstract fun printAbs(a)
}

/** * Example */
fun testRun(a) {
    // Inherits the object expression of the interface
    var obj = object: InterfaceBase {
        override fun print(i: Int) {
            println("InterfaceBase i=$i")
        }
    }
    obj.print(10)

    // There are no inherited object expressions
    val obj2 = object {
        fun test(a) {
            println("obj2 test")
        }
    }
    obj2.test()

    // Inherits an object expression from an abstract class
    val obj3 = object: AbsInterfaceBase() {
        override val age: Int
            get() = 12

        override fun printAbs(a) {
            println("abs age=$age")}}// Inherit the object expression of the abstract class and interface
    val obj4 = object: InterfaceBase, AbsInterfaceBase() {
        override val age: Int
            get() = 12

        override fun printAbs(a) {
            println("abs age=$age")}override fun print(i: Int) {
            println("interface age=$age, i=$i")}}}Copy the code

Kotlin object expressions (anonymous objects) can only be identified by their true type within the scope of local variables or member variables that are modified by private. If an object expression (anonymous object) is treated as the return type of a non-private method or as a non-private type, then the true type of the method or property is the parent type of the anonymous type. If no parent type is declared, then the type is Any. In this case, Any member declared in an anonymous object is inaccessible. The last example was a locally scoped anonymous object. Here is an example of a member variable:

class TClass {
    // There is a private modifier
    private var obj = object {
        fun out(a) {
            println("obj out method")}}fun test(a) {
        obj.out(a)//obj out method}}class TClass1 {
    // There is no private modifier
    var obj = object {
        fun out(a) {
            println("obj out method")}}fun test(a) {
        Unresolved reference: out // Error Unresolved reference: out
        //obj.out()}}Copy the code

Java anonymous inner classes can only access the members of the outer class through Final, and they cannot modify the outer class members. Kotlin’s object expressions do not have this limitation and can directly access or modify the members of the outer object. The following cases:

Click index is 1 click index is 2 */
fun testRun(a) {
    var index: Int = 1
    val obj = object {
        fun click(a) {
            println("click index is $index") // Access external members
            ++index // Modify the external member
        }
    }
    obj.click()
    obj.click()
}
Copy the code

If the object is an instance of a Java functional interface (that is, an interface that has only one abstract method), it can be invoked through a lambada expression, in which lambada is preceded by the type of the interface.

Note in particular: If the object is an instance of a functional Java interface (that is, a Java interface with a single abstract method), you can create it using a lambda expression prefixed with the interface type. Before Kotlin 1.4, remember to refer to Java functional interfaces, not the functional interfaces defined in Kotlin. The latest version of Kotlin does not have this limitation, and functional interfaces defined in Kotlin can also be used.

The following is a demonstration case:

/ / 【 craftsman if water to add WeChat yanbo373131686 contact me, concern WeChat public number: code farmers a daily topic Without permission is strictly prohibited reproduced, https://blog.csdn.net/yanbober]

class View {
    private var listener: OnClickListener? = null

    fun setOnClickListener(l: OnClickListener) {
        this.listener = l
    }

    // Kotlin defines a functional interface
    interface OnClickListener {
        fun click(view: View)}}/** * Use cases */
fun testRun(a) {
    val view = View()
    // Anonymous object mode
    view.setOnClickListener(object: View.OnClickListener {
        override fun click(view: View) {
            println("view is ${view.javaClass}")}})// Interface OnClickListener does not have constructors
    //view.setOnClickListener(View.OnClickListener {
    / /})

    //JButton and ActionListener are Java interfaces
    val button = JButton()
    // Anonymous object mode
    button.addActionListener(object: ActionListener {
        override fun actionPerformed(e: ActionEvent?). {
            println("666")}})//lambada expression
    button.addActionListener(ActionListener {
        println("777")})//lambada short for 1
    button.addActionListener({
        println("888")})// Lambada - 2(Kotlin function call feature)
    button.addActionListener {
        println("999")}// Lambada expressions use arguments
    button.addActionListener { e ->
        println("action $e")}}Copy the code

Object expressions differ from object declarations and associated objects:

  • Object expressions are initialized or executed immediately.
  • Object declarations are lazily initialized at first access.
  • An associated object is initialized when its corresponding class is loaded, corresponding to Java’s static initialization.

Kotlin enumeration

The Kotlin enumeration definition is almost exactly the same as the Java enumeration definition, so there’s nothing special about it, so here’s an example:

/ / 【 craftsman if water to add WeChat yanbo373131686 contact me, concern WeChat public number: code farmers a daily topic Without permission is strictly prohibited reproduced, https://blog.csdn.net/yanbober]

enum class Reason {
    ERROR, WARNING, INFO
}

enum class Reason1(val msg: String) {
    ERROR("this error"),
    WARNING("this waring"),
    INFO("this info")}enum class Reason3(val msg: String) {
    ERROR("this error"),
    WARNING("this waring"),
    INFO("this info");

    fun getMessage(a): String {
        return this.msg
    }
}

enum class Reason4() {
    ERROR {
        override fun getMessage(a): String = "this error"
    },
    WARNING {
        override fun getMessage(a): String = "this waring"
    },
    INFO {
        override fun getMessage(a): String = "this info"
    };

    abstract fun getMessage(a): String
}

This error this waring this info */
fun testRun(a) {
    val reasons = Reason3.values()
    reasons.forEach { println(it.getMessage()) }
}
Copy the code

This concludes the summary of the omnidirectional differences associated with kotlin objects.