This is the second in Kotlin’s series

  • class
  • Class member and visibility modifier
  • The constructor
  • Inheritance, abstraction, and interfaces
  • Expanding method
  • Null type safety
  • Intelligent type conversion
  • Lazy initialization of a class attribute
  • Agent Delegate
  • The singleton object
  • The inner class
  • Data class
  • Enumeration class Enum class
  • Sealed class
  • Inline class inline class

A, classes,

In Java, classes are based on Object, while in Kotlin classes are based on Any, and all classes inherit Any by default

The Any class has only three methods: equals, hashCode, and toString.

package kotlin
 
public open class Any {
   
    public open operator fun equals(other: Any?).: Boolean
 
    public open fun hashCode(a): Int
 
    public open fun toString(a): String
Copy the code
  • The keyword for declaring a class is class

  • Declare the format of the class

class Test{
    / / properties....// constructor./ / function./ / inner classes. . }Copy the code
  • Note that braces can be ignored if the class has no structure
class Test
Copy the code

Let me give you a quick example

class Animal {
    fun eat(a) {
        println("Every day is eating, eating, eating.")}fun move(a) {
        println("Every day is a wave.")}}fun main(a) {
    var ani = Animal()
    ani.eat()
    ani.move()
}
Copy the code

The output

Every day is eating and eating and every day is rolling and rollingCopy the code

.

A few things to note about the KT class

Kotlin’s interface can contain attribute declarations. Kotlin’s default declarations are fianl and public, which means the default is not allowed to be inherited

Kotlin’s classes and methods are final by default (public final, to be exact), which means that they are not allowed to be inherited by default. A class that wants to be inherited needs to be explicitly declared open. (Java classes are not final by default, i.e. inherited arbitrarily).) A method that wants to be overridden also needs to be declared open. If the class is not open, members of the class are not allowed to be open.

open class Animal {
    fun eat(a) {
        println("Every day is eating, eating, eating.")}open fun move(a) {
        println("Every day is a wave.")}}// inheritance from kt uses:, not extends
class Bird : Animal() {
    // the eat method is not open and cannot be overwritten

    // The move method can be overwritten
    override fun move(a) {
        super.move()
        println("Always up there, naughty bird can't get drunk.")}}fun main(a) {
    var b = Bird()
    b.eat()
    b.move()
}
Copy the code

The output

Every day is eat eat every day is wave wave wave always in the sky, naughty bird can not drinkCopy the code

Of course, in addition to Open, we can also use abstract to make classes abstract, which can also be inherited. We’ll talk about the abstract for another time.

Class members and access modifiers

The members of the class

  • Constructor and initializer blocks (Constructors and initializer blocks)
  • Function (Functions)
  • Properties (Properties)
  • Nested and inner classes (Nested and Inner Classes)
  • Data object (Object Declarations)

Access modifier/visibility modifier

Class modifiers include classModifier and _accessModifier_:

ClassModifier: Indicates the attribute modifier of a class.

  • Abstract // Abstract class
  • Final // class is not inheritable, default property
  • Enum // Enumeration class
  • Open // Classes are inheritable, and classes are final by default
  • Annotation // Annotation class

AccessModifier: Access permission modifier

  • Public kt default modifier, globally visible
  • Protected Protected modifier, class and subclass visible
  • Private Private modifier. Modifiers within a class are visible only to the class. Modifiers outside the class are visible to the file
  • Visible in internal modules (this is unique to KT, not available in Java)

In kotlin, the default is public. In Kotlin, the default is default. 3. Protected can only be used to modify members.

For protected, Java is visible within the package, whereas Kotlin is visible within the class, which is different. Of course, subclasses are always visible, and in Kotlin protected cannot be used to modify classes

// File name: example.kt
package foo

private fun foo(a) {} // visible in example.kt

public var bar: Int = 5 // This property can be seen everywhere

internal val baz = 6    // Visible in the same module
Copy the code

Kotlin class constructor

The main feature of kotlin class constructors is that they are divided into primary constructors and secondary constructors.

  • Primary constructor – a neat way to initialize a class (there is only one)
  • Secondary constructor – to place additional initialization logic (multiple allowed)

In Kotlin, the constructor key user declares the constructor, which is allowed to be omitted under certain conditions.

In Kotlin, constructors are identified by the constructor keyword; for the primary constructor, its position is declared in the class title, and for the secondary constructor, its position is in the class.

Principal constructor

  • Principal constructorisThe class headerPart of the
  • withparenthesesThe enclosedThe code blockisPrincipal constructor
  • The main constructor is allowed to have a default value, which is exactly what it was initialized to
  • The syntax of the main constructor is constrained to contain no code. If there is an initialization operation, it should be placed ininitInside this particular block of code

You can also assume that the initialization of the main constructor is either set to default values or must be done in the init block

  • Declaring a constructor is usually requiredconstructorKeyword, but when the constructor keyword does notannotationsandVisibility modifierWhen applied to it, the constructor keyword can be omitted.
  • When we define a class without declaring a primary constructor, Kotlin generates a primary constructor with no arguments by default, just like In Java.

All right, let’s go to code.

// Constructor (name:String,age:Int
// The main constructor is part of the class header
class Person constructor(name:String,age:Int,hobby:String = "Sleep") {val pName:String
    val pAge:Int
    val pHobby:String
    // this.name = name cannot be written like this, the initialization operation must be in init

    // init code block user initializes constructor
    init {
        this.pName = name
        this.pAge = age
        this.pHobby = hobby
        println("Name:$pName")
        println("Age:$pAge")
        println("Interest:$pHobby")}}fun main(a) {
    var tony = Person("Wang ha ha!".18)} Output: Name: Wang Haha Age:18Hobby: SleepingCopy the code

For the above Person class, constructor can be omitted.

// For example, this class can omit constructor
// Because this constructor has no annotations or visibility modifiers
//class Animal constructor (move:String){
class Animal(move:String){
    val aMove:String;
    init {
        this.aMove = move; }}// This constructor cannot be omitted
class Animal2 private  constructor(move:String){
    val aMove:String;
    init {
        this.aMove = move; }}Copy the code

subconstructor

In Kotlin, a class can also contain one or more subconstructors. They are created using the constructor keyword.

Subconstructors are not common in Kotlin. The most common use of secondary constructors comes when you need to extend a class that provides multiple constructors that initialize the class in different ways.

Mainly look at the following points:

  • subconstructorMust be usedconstructordefine
  • In Kotlin, you can also usethis()Come fromIn the same classtheAnother constructorCall the constructor, as in Java.
  • By using it in a classPrimary and secondaryClass constructor,Secondary constructorNeed to beDelegate to (delegate to) the primary constructorThat is, the secondary constructor willdirectlyorindirectCall the primary constructor. usethis()The keyword authorizes (delegates) another constructor in the same class.
  • The subclass constructor can passsuperThe keyword calls the parent class constructor

The subconstructor can be used in roughly two ways

  • Use the subconstructor directly
  • Primary and secondary constructors are used together

Use the subconstructor directly

class Car{
    constructor(name: String) {
        println("The name of the car is$name")}}fun main(a) {
    var car = Car(Bumblebee} The name of the output car is BumblebeeCopy the code

As above, there are only subconstructors.

Primary and secondary constructors are used together

Let’s look at this in the code

  • In Kotlin, you can also use this() to call a constructor from another constructor of the same class, such as in Java.

  • By using the primary and secondary constructors in a class, the secondary constructor needs to delegate to the primary constructor, that is, the secondary constructor calls the primary constructor directly or indirectly. Use the this() keyword to authorize (delegate) another constructor in the same class.

Class Animal(val name: String) {init {val outPutName =" $name" println(outPutName)} // Use this() to call the constructor from another constructor of the same class (such as in Java). // Delegate directly to the main constructor. Constructor (name: String, age: Int): This (name) {println(" direct delegate: name: age: $age")} constructor(name: String, age: Int, move); String): This (name, age) {println(" name: $name, age: $age ") $move ")}} to fun the main () {var bird1 = Animal (" main wasp ") println (" = = = = = = = = = = ") var bird2 = Animal (" wasp "1, 18) Println (" = = = = = = = = = = ") var bird3 = Animal (18, "the hornets # 2", "take off")}Copy the code

.

  • A subclass constructor can call the parent class constructor through the super keyword. (Error if not done)
open class Car{
    constructor(name: String) {
        println("The name of the Car is$name")}}// Subclasses use super to call the constructor of their parent class
class NiceCar : Car{
    constructor(name: String,spec: String):super (name) {
        println("The name of NiceCar's car is$name. Skills are:$spec")}}fun main(a) {
    var niceCar = NiceCar(Bumblebee."Fly in the sky every day."
}
Copy the code

Constructors and inheritance

Inheritance, which I’m going to do later, but I’m going to put constructors together.

  • When the parent class existsPrincipal constructorSubclasses must also have a constructor (either a primary constructor or a secondary constructor), otherwise an error will be reported

open class Animal(name: String){

}
/ / the main structure
class Bird(name: String) : Animal(name) {

}
/ / structure
class Bird : Animal {
    constructor(name: String) : super(name)
}
Copy the code

.

  • When the parent class has more than one constructor, the primary constructor of the subclass generally implements the constructor with the most arguments in the parent class. Subclasses with few arguments can be referenced with the this keyword.
open class Animal{
    constructor(name:String)
    constructor(name:String,gender:String)
    constructor(name:String,gender:String,age: Int)}class Bird(name: String, gender: String, age: Int) : Animal(name, gender, age) {
    constructor(name: String) : this(name,"".0)}Copy the code

Inheritance, abstraction, and interfaces

inheritance

Inheritance requires the use of open and: a class that wants to be inherited needs to be declared open. Inheritance requires :(extends in Java) methods and properties of a class that also need to be declared open if they want to be overwritten.

Here’s a little code to demonstrate open: notice if methods and properties are open

open class Animal{
    var hobby:String = "Hobby"
    open var name:String ="Animal";

    fun move(a){
        println("Movement")}open fun eat(a){
        println("Eat eat eat.")}}// inherit
class Bird1 : Animal() {}// inherit
class Bird2 : Animal() {
    // This can be copied because of open
    // Hobby cannot override without open
    override var name: String
        get() = super.name
        set(value) {"Birds!"}

    // This can be copied because of open
    // move cannot override
    override fun eat(a) {
        super.eat()
        println("Eat some worms.")}}fun main(a) {
    var b1 = Bird1();
    var b2 = Bird2();

    println(b1.name)
    println(b1.hobby)
    b1.eat()
    b1.move()

    println("= = = = = = = = = = = = = = =") println(b2.name) println(b2.hobby) b2.eat() b2.move()Copy the code

.

  • The open subclass of the parent class can override that, I know, but there is no open modifier in the parent class, and a subclass cannot have a function name with the same name.

An abstract class

  • In Kotlin, abstractions can be divided into abstract classes, abstract functions and abstract attributes. The members of abstract classes include abstract functions and abstract attributes. Members of an abstract class are defined but not implemented, and they cannot be implemented, otherwise they are nonabstract.

  • Declarative abstractions are decorated with the abstract keyword. Subclasses of an abstract class must completely override the abstract properties and functions with the abstract modifier. Non-abstract functions and attributes are allowed in abstract classes.

  • Abstract classes are open modified by default and can be inherited directly. Unlike normal classes that need to be declared open for inheritance

  • Properties of abstract classes can store state. (Interface attributes cannot hold state and must be abstract)

abstract class Car{
    // Non-abstract signs and functions that can have initialization values and implementations
    // whoMake has a value, indicating that the abstract property can save the state
    var whoMake = "Made by man."
    // A normal function in an abstract class, final by default, is not specified as open and cannot be overwritten by subclasses
    fun oKCar(a){
        println("Checked out")}// An abstract member cannot have an initialization value
    abstract var name:String
    // Abstract methods cannot have implementations ({})
    abstract fun spec(spe:String)
}

class NiceCar : Car() {
    // It must be copied
    override var name:String =  "Skycar"

    // It must be copied
    override fun spec(spe: String) {
        println(spe)
    }
}

fun main(a) {
    var niceCar = NiceCar()
    println(niceCar.whoMake)
    niceCar.oKCar()

    println(niceCar.name)
    niceCar.spec("Fly in the sky every day.")} Cars fly in the sky every dayCopy the code

Interface class

Kt uses the keyword interface to define interfaces. A class can implement one or more interfaces

  • The interface implementation uses the keyword: colon (:). This is different from Java. Java uses the implements keyword to use interfaces.)

  • Interfaces can only inherit from interfaces, not from classes

  • Kt interface members support private and public access modifiers, the default public (Java interface members can only be public)

  • Kotlin’s interface can have either abstract or non-abstract methods. A function in an interface with a struct/method body (with {}) is no longer an abstract method.

  • Functions in interfaces that have no method body (public abstract by default) must be overwritten. If the function has a method body (public open), it can not be overwritten.

  • The interface could not save state.

  • Interfaces can have properties that must be declared abstract, or they can provide accessor implementations (provide a GET method that is no longer abstract).

  • A backing field cannot be assigned to a property on an interface.

Functions in interfaces

interface SimpleInterface{
    // Define a method with no arguments and no return value
    fun fun1(a)
    // Define a method with parameters
    fun fun2(num: Int)
    // Define a method that takes parameters and returns values
    fun fun3(num: Int) : Int

    // The following two methods have structures, so they can not be overridden
    // Define a method with no arguments and a return value
    fun fun4(a) : String{
        return "fun4"
    }
    // Define a structless function. The curly braces can be omitted
    fun fun5(a){
        // If no expression exists in the function, the curly braces can be omitted.
        // Like fun1}}class Demo2 : SimpleInterface{
    // fun1, fun2, and fun3 must be overridden (because they do not return values in the interface), otherwise an error is reported
    override fun fun1(a) {
        println("I'm the fun1() method")}override fun fun2(num: Int) {
        println("I'm the fun2() method, and my argument is$num")}override fun fun3(num: Int): Int {
        println("I'm the fun3() method, and my argument is$numAnd return a value of type Int.")
        return num + 100
    }

    override fun fun4(a): String {
        println("I'm a fun4() method and I return a String value.")

        /* The fun4() method in the interface returns the "fun4" string by default. You can use super.fun4() to return the default value, or you can omit the super keyword and return the string */
        return super.fun4()
    }

    /* The fun5() interface has a structure, so it doesn't need to be overwritten, as does fun4()
    // override fun fun5() {
    // super.fun5()
    / /}
}

fun main(a) {
    var demo = Demo2()

    demo.fun1()
    demo.fun2(5)
    println(demo.fun3(3))
    println(demo.fun4())
    // Can be called directly without overwriting the methodDemo.fun5 ()} Output: I am the fun1() method I am the fun2() method and my argument is5I'm the fun3() method, and my argument is3And return oneIntThe value of the type103I am the fun4() method and return a String value of fun4Copy the code

Attributes in interfaces

Interfaces can have properties that must be declared abstract, or provide accessor implementations (provide a GET method that is no longer abstract).

interface SimpleInterface{
    Properties in an interface are abstract by default
    val num: Int Public abstract val num: Int

    // Non-abstract attributes provide accessors
    // Equivalent to public Open Val hobby: String
    val hobby: String
        get() = "Some hobbies"

    fun fooNum(a) {
        print(num)
    }
}

class Demo2 : SimpleInterface{
    // Since num is abstract, we must duplicate it here
    override val num: Int
        get() = 100
}

fun main(a) {
    var demo2 = Demo2()
    println(demo2.num)
    // Because Hobby provides the accessor get, we can access it directly
    println(demo2.hobby)
    // Call the method in the interface
    demo2.fooNum()
}
Copy the code

.

A backing field cannot be assigned to a property on an interface.

Interface inheritance

An interface can be derived from other interfaces to provide both implementations of base type members and declarations of new functions and properties. Naturally, classes that implement such an interface need only define the missing implementation

interface Person : Named {
    var firstName: String
    var lastName: String

    override val name: String get() = "$firstName $lastName"
}
// Employee is implemented from Person, and Person is implemented from Named
class Employee : Person {
    // Employee does not need to overwrite the name attribute

    // Employee should override the name property of the grandparent class
    Employee does not need to overwrite the name attribute because the parent Person has already been overwritten
    // Of course, Employee can copy the name itself if she wants
    override var firstName: String = ""
    override var lastName: String = ""
}

fun main(a) {
    var emp = Employee()
    emp.firstName = "The sea"
    emp.lastName = "Li"Println (emp.name)} Output: Sea plumCopy the code

Resolving coverage Conflicts

If interface A and interface B both have the same method, then c is derived from interface A and interface B. What if?

interface A {
    fun foo(a) { println("A-foo")}fun goo(a)
    fun move(a)
}

interface B {
    fun foo(a) { println("B-foo")}fun goo(a) { println("B-goo")}fun move(a)
}
// We notice that both A and B implement foo, and that foo in both cases is no longer abstract
// A's goo method is an abstract method and A's goo is A non-abstract method
// Both A and B's move methods are abstract

class C : A {
    // Because both goo and move in A are abstract
    override fun goo(a) { println("C-goo")}override fun move(a) {println("C-move")}}class D : A.B {
    // Multiple inheritance, both A and B implement foo, so this needs to be overridden
    override fun foo(a) {
        super<A>.foo()
        super<B>.foo()
        println("D-foo")}// Multiple inheritance, because B's goo method has its own implementation, we just need to copy superB
    override fun goo(a) {
        super<B>.goo()
        println("D-goo")}// Multiple inheritance, because A and B move are abstract, so we don't need superA or B here
    override fun move(a) {
        println("D-move")}}fun main(a) {
    var c = C();
    c.foo()
    c.goo()
    c.move()
    println("= = = = = = = = =")
    vard = D(); D.foo () d.goo() d.move()} Output: a-foo c-goo c-move ========= a-foo b-foo d-foo b-goo d-goo d-moveCopy the code

Fifth, the expansion method

Expansion, this is a good thing.

Kotlin supports dynamically adding attributes (extended attributes) and methods (extended methods) to a class without modifying the class code.

For example, a tripartite library you think is great, but you use it to slap on the thigh and say why it doesn’t have XXX methods, but you have code that can’t change it, then it’s time to expand.

Extension methods perform static parsing (compile time) and member methods perform dynamic parsing (run time).

The principle of

When kotlin extends attributes and methods, it looks like it adds members to the class dynamically. It doesn’t actually modify the extended class. What Kotlin does is define a function that performs static resolution when an object of the extended class calls an extension method, statically resolving the call to a function call.

format

In fact, very simple, is to put the method name in front of the type.

Have a feel for it

class Person{
    fun eat(a){
        println("Eat eat eat.")}}fun Person.playMan(a){
    println("Cynical")}fun main(a) {
    varPer = Person() per.eat() per.playman ()Copy the code

Expanding method

When expansion meets the real Monkey King

If the extension method of the extended class has the same name and argument as the member method of the class, the class object will call the member method.

The member method takes precedence over the extension method

class Person{
    fun eat(a){
        println("Eat eat eat.")}}fun Person.playMan(a){
    println("Cynical")}fun Person.eat(a){
    println("Surprised")}fun main(a) {
    var per = Person()
    per.eat() // When you meet someone, you lose your nerve
    per.playMan()
}
Copy the code

The system’s own classes can also be extended

For example, add an extension method to the system class String

Fun String.lastIndex() = length-1Copy the code

The str.lastindex () method executes as follows: 1. Check the STR type (found to be String);

Check if String defines a lastIndex() member method. If so, compile it directly.

3. If String does not define lastIndex(), Kotlin starts to find if the program has extended the lastIndex() method for the String class. If it has, it will execute the extension.

The lastIndex() member method is not defined and the extension method is not defined.

When both parent classes extend the same method

Because static calls to extension methods are performed at compile time, the extension method of the parent class is called if both the parent class and the subclass extend an extension method of the same name, and the reference type is the parent class.

/** ** parent */
open class ExtensionTest

/** * subclass */
class ExtensionSubTest : ExtensionTest(a)/** * The parent extends the test method */
fun ExtensionTest.test(a) = println("Parent class extension method")

/** * subclasses extend the test method */
fun ExtensionSubTest.test(a) = println("Subclass extension method")

fun main(a) {
    val father : ExtensionTest = ExtensionTest()
    father.test()// Call the parent class extension method

    // Note the type ExtensionTest
    val child1 : ExtensionTest = ExtensionSubTest()
    child1.test()// The reference type is the superclass type, and the extension method of the superclass is statically called at compile time

    // Note the type ExtensionSubTest
    val child2 : ExtensionSubTest = ExtensionSubTest()
    child2.test()// Call the extension method of the subclass} output: parent extension method Parent extension method subclass extension methodCopy the code

Nullable type extension method

Within the extension function, this can be used to determine whether the receiver is NULL, so that the extension function can be called even if the receiver is NULL.

funAny? .toString(a): String {
    if (this= =null) return "null"
    // After null detection, "this" is automatically converted to a non-null type, so the following toString()
    // parse as a member function of Any class
    return toString()
}
fun main(arg:Array<String>){
    var t = nullPrintln (t.tostring ())}null

Copy the code

Define extension methods as class members

A class that defines an extension method or attribute for another class can only call the extension method from the object of the class being extended.

An extension defined as a class member belongs to the extended class, so it calls a member of the extended class directly in the extension method (this can be omitted), and can also call a member of the extended class directly because it is in the same class.

/** * define a class that contains the test method */
class ExtensionTest {
    fun test(a) = println("Test method for ExtensionTest")}/** * define a class that contains the test method and an extension of ExtensionTest */
class ExtensionTest2 {
    val a = "a"
    fun test(a) = println("Test method for ExtensionTest2")
    fun ExtensionTest.func(a) {
        println(a)// Call a member of the extension class
        test()// Call a member of the extended class, equivalent to this.test()
        this@ExtensionTest2.test()// Calls with the same name need to be called with the this@class name
    }

    fun info(extensionTest: ExtensionTest) {
        extensionTest.func()
    }
}

fun main(a) {
    val extensionTest = ExtensionTest()
    valExtensionTest2 = extensionTest2 () extensionTest2.info(extensionTest)} Output: a test method of extensionTest extensionTest2 test methodCopy the code

Anonymous extension function with receiver

  • Extension method (fun class name. Removing the method name is what is called an anonymous extension function with a receiver. The receiver is the class itself, in the form of fun Int.() : Int.
/** * defines an empty class */
class ExtensionTest

/** * defines an anonymous extension function */ with a receiver for the empty class
var noNameExtensionFun = fun ExtensionTest.(param: String): String {
    println(param)
    return "I'm the return value from an anonymous extension function with a receiver."
}

fun main(a) {
    val extensionTest = ExtensionTest()
    println(extensionTest.noNameExtensionFun("Parameters passed to anonymous function with receiver"))// Use anonymous extension functions} output: The argument passed to the anonymous function with a receiver IS the return value from the anonymous extension function with a receiverCopy the code

Like normal functions, anonymous extension methods have function types. In the above example, the function type is ExtensionTest.(String) -> String

Expand the properties

  • Kotlin allows you to dynamically extend attributes of a class by adding get and set methods with no hidden fields (no direct assignment).

The extension attributes do not actually add attributes to the class, but instead calculate attributes for the class using the get and set methods.

Limitations of extended attributes: 1. Extended attributes cannot have initial values; 2. The field keyword cannot be used to access the background field for extended attributes; Val must provide get methods and var must provide get and set methods.

To copy the code

/** * define a class that contains properties param1 and param2 */
class ExtensionTest(var param1: String, var param2: String)

/** * for extensionParam */
var ExtensionTest.extensionParam: String
    set(value) {
        param1 = "param1$value"
        param1 = "param2$value"
    }
    get() = "$param1-$param2"
Copy the code

Anonymous Extensions and Lambda

Lambda expressions can be used if the receiver type can be inferred from the context

/** * defines an empty class */
class ExtensionTest

/** * define a function that takes ExtensionTest.(String) -> String, which extends an anonymous extension function */ for ExtensionTest
fun test(fn: ExtensionTest. (String) - >String) {
    val extensionTest = ExtensionTest()
    println("${extensionTest.fn("Anonymous extension function passing parameters")}")}fun main(a) {
    test {
        println(it)
// println("123")
        "Return value (anonymous extension function)"}} Output: the anonymous extension function passes the parameter return value (anonymous extension function)Copy the code

Six, empty type safety

Let’s start with an example

// name is a variable that cannot be null, and cannot be assigned to null
var name : String = ""

// Hobby is nullable because String?
var hobby : String? = ""
Copy the code

Kotlin space types are divided into: nullable type and non-nullable type;

  • Nullable typesUse:?The NullPointerException (NPE) operator is used to declare nullable types.
  • Not empty type: Regular variables cannot contain NULL

Let the operator

If you want to perform an operation only on non-null values, safely call the operator? Can be used with let

Example: For example, filter out elements of a set that are not empty

fun main(a) {
    // Do not write the let operator as usual
    fun filterArr1(arr: Array<String? >):Unit{
        for (item in arr){
            if (item==null) {continue
            }else{
                println("Non-empty elements are ->"+item)
            }
        }
    }

    / /? . Writing
    / /? .let data is non-null
    fun filterArr2(arr:Array<String? >):Unit{
        for (item inarr){ item? .let { println("Non-empty elements are ->"+item)
            }
        }
    }

    var testArray = arrayOf("What"."Yes"."Happy".null."Star".null)
    filterArr1(testArray)
    println("= = = = = = = = = =") filterArr2(testArray)} Output: Non-empty elements for -> what non-empty elements for -> Yes non-empty elements for -> Happy non-empty elements for -> planet ========== Non-empty elements for -> What non-empty elements for -> Yes non-empty elements for -> Happy non-empty elements for -> planetCopy the code

Equivocations operator (? :!!!!! As)

So the equivocative operator is divided into,? :!!!!! as as?

  • ? :if? :The Elvis operator returns the left-hand expression if it is not empty, or the right-hand expression if it is not.
  • !!!!!To use a nullable variable, add!! Throws a null-pointer exception when the variable is null
  • As and as?There are 2 cases
    • When using AS, a ClassCastException is thrown if the cast fails
    • When using the as? If the type conversion fails, null is returned without an exception

So! Throw an exception!! Use as little as possible. Use as? ? .? Let’s do it


Let’s do some examples

  • To a? :

If? The Elvis operator returns the left-hand expression if it is not empty, or the right-hand expression if it is not.

fun main(a) {
    var str: String? = null
    vallen1 = str? .length ? : -1
    println(len1) / / output 1

    str = "hello"
    vallen2 = str? .length ? : -1
    println(len2) / / output 5
}
Copy the code
  • To a!!!!!

To use a nullable variable, add!! If the variable is null, the null pointer exception NPE is thrown

fun main(a) {
    var s: String? = nullprintln(s!! .length)// The runtime returns an error
}
Copy the code

Null pointer

  • An as and an AS?

    • When using AS, a ClassCastException is thrown if the cast fails
    • When using the as? If the type conversion fails, null is returned without an exception

asAn example of

fun main(a) {
    varname:String? =12 as String

    // The runtime returns a ClassCastException
    // java.lang.Integer cannot be cast to java.lang.String
    println(name)
}
Copy the code

as?An example of

fun main(a) {
    varname:String? =12 as? String
    println(name)  / / output is null} output:null
Copy the code

Intelligent type conversion

Kotlin’s type-only conversion can be divided into two cases

  • Automatically infer the type and convert
  • A safe conversion of an empty type

Automatically infer the type and convert

In Kotlin, once the type is determined, you can call a function of a subclass directly from an object of the parent class

open class Person{}

class Student: Person() {
    fun study(a){
        println("Learn something")}}fun main(a) {
    var person:Person = Student()
    if(person isStudent){person.study()}Copy the code

If you want to do this in Java, it is a lot of trouble.

Need to be like this:

public class Person{}public class Student extends Person{
    public  void study(a){
        System.out.println("I'm learning a new language Kotlin!"); }}public static void main(String[] args){
    Person person = new Student();
    if(person instanceofStudent){ ((Student) person).study(); }}Copy the code

What if you don’t do intelligent casting?

In the example above, if we call study by forcing the parent class to subclass without making a type judgment, we will get an error.

fun main(a) {
    val person = Person()
    (person as Student).study()
}
Copy the code

There is a solution to this, of course, and that is nullable conversions.

A safe conversion of an empty type

So the safe conversion of the empty type is just the equivocative operator as? And as

val person: Person = Person()
val student:Student? =person as? Student
println(student) // null
Copy the code

Lazy initialization of class attributes

When you declare an attribute in Kotlin, you must initialize it, otherwise the editor will report an error.

class Cat() {
    val name: String / / an error
    var baby: Int / / an error

    val hobby: String = "Sleep" / / is not an error
}
Copy the code

But a lot of times in development we don’t initialize properties immediately.

For example, when defining a control name, we don’t initialize the controller as fingViewById. Instead, we initialize the controller in the page lifecycle function. Kotlin offers developers a way to delay initialization.

There are two types of delayed initialization in Kotlin.

  • lateinit
  • by lazy

lateinitUsed in var

.

There are a few things to note about using LateInit

  • Lateinit can only be used with var (val is not), so it is often used in conjunction with var

  • Lateinit can only be used to modify class attributes, not local variables

  • Lateinit cannot modify basic data types if int (kotlin basic type: Double, int, Float, Long, Short, Byte)

  • Lateinit is used for variables that can only be fetched or initialized in a lifecycle process, such as onCreate() on Android

  • The lateInit var function is to ensure that compile-time checks don’t report errors because the attribute variable has not been initialized. It’s not really empty. Kotlin believes that when a developer explicitly uses the lateInit var keyword, he will also initialize the attribute object at some reasonable time later.

class Cat() {
    // lateinit Delay initialization
    lateinit var catName: String / / an error
}

fun main(a) {
    var cat = Cat()
    //println(cat.catname) // is not initialized, so using it directly is problematic
    Error: Lateinit property catName has not been initialized
    
    // When initialized, the call is ok
    cat.catName = "Blue cat"Println (cat.catname)} Output: blue catCopy the code

Lateinit is used on Android

private lateinit var s: String
private val ss:String by lazy { "132" }

fun main(a) {
    print("Lazy loading ss =$ss")
    try {
        print("No initialization is s =$s  \n")   // Must be initialized before it can be used
    }catch (e:Exception){
        e.printStackTrace()
    }
    s = "123456"
    print("After initialization, s is equal to$s")}Copy the code

One more

class MainActivity : AppCompatActivity() {

    private lateinit var bt: Button

    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        bt = findViewById(R.id.bt)
        bt.setOnClickListener {
            Toast.makeText(baseContext, "click", Toast.LENGTH_SHORT).show()
        }
    }
}
Copy the code

.

By lazy is used in val

  • By Lazy is itself an attribute delegate. The key word for the attribute delegate is BY

  • Lazy can only modify val, an immutable variable

  • Applies to singleton mode (if-null-then-init-else return), and the delegate method executes if and only if the variable is called for the first time.

  • By lazy can be used on class attributes or local variables.

For example

class Cat() {
    The delegate method is executed if and only if the variable is called for the first time.
    val name by lazy {
        println("Only called once")
        The Cat with the Big Face}}fun main(a) {
    val lazyCat = Cat()
    // lazycat.name when the delegate method executes
    val name = lazyCat.name
    // lazycat1.name was executed on the delegate method and is no longer executed
    val name1 = lazyCat.name
    
    println("= = = =")
    println("lazy-name:$name")
    println("lazy-name:$name1")} Output: called only once ==== lazy-name: large-faced cat lazy-name: large-faced catCopy the code

Use in Android

class MainActivity : AppCompatActivity() {

   private val bt by lazy {
        findViewById<Button>(R.id.bt)
    }
    
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        bt.setOnClickListener {
            Toast.makeText(baseContext, "click", Toast.LENGTH_SHORT).show()
        }
    }
}
Copy the code

Three delay modes for lazy

  • LazyThreadSafetyMode.SYNCHRONIZED(Default mode)
  • LazyThreadSafetyMode.PUBLICATION(Multiple Initializer available)
  • LazyThreadSafetyMode.NONE(No lock for synchronization, multiple instances)

When using lazy lazy initialization, Kotlin provides three modes, source code as follows:

public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
    when (mode) {
        LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
        LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
        LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
    }

private val sss:String by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { "The last function can be put outside." }
Copy the code

.

When we don’t have the mode of cases, the default LazyThreadSafetyMode. SYNCHRONIZED thread safe mode. The source code is as follows:

public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
Copy the code

By lazy implements a singleton

A singleton of the double check lock pattern

/ / Java implementation
public class SingletonDemo {
    private volatile static SingletonDemo instance;
    private SingletonDemo(){} 
    public static SingletonDemo getInstance(){
        if(instance==null){
            synchronized (SingletonDemo.class) {if(instance==null){ instance=new SingletonDemo(); }}}returninstance; }}/ / kotlin implementation
class SingletonDemo private constructor() {
    companion object {
        val instance: SingletonDemo by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
        SingletonDemo() }
    }
}
Copy the code

There are several things involved here, lambda, higher-order functions (functions that use functions as arguments or return values), and attribute delegates.

Delegate

The proxy and delegate patterns are similar, but they cannot be considered the same.

  • Delegation Pattern
  • Proxy Pattern Proxy Pattern

Kotlin uses the by keyword to implement the delegate pattern

The delegate pattern is a basic skill in the software design pattern. In the delegate pattern, two objects participate in processing the same request, and the object receiving the request delegates the request to another object

Refer to this article for information about agents and delegates

[Kotlin | entrusted (Delegation) explanation] (juejin. Cn/post / 700932…

The singleton object

Multiple implementations of kt singleton

  • Hangry singleton
  • Hangry singleton
  • Thread-safe slob style
  • Double Check lock
  • Static inner class

Hangry singleton

Kotlin introduced a type called object to make it easy to implement the singleton pattern.

// Super simple singleton
object SimpleSington {
  fun test(a){}}// call in Kotlin
SimpleSington.test()
Copy the code

Yes you read that right, a line of Object SimpleSington{} implements a singleton

This is this is a syntactic sugar for KT.

The real internal implementation looks like this

public final class SimpleSington { public static final SimpleSington INSTANCE; private SimpleSington() { INSTANCE = (SimpleSington)this; } static { new SimpleSington(); }}Copy the code

Thus Kotlin’s ultra-simplified singleton implementation is omitted and used as follows

  • Explicitly declare a static instance variable
  • Make the constructor private

But when Java and Kotlin are mixed, calls in Java code need to be taken care of, using the following

SimpleSington.INSTANCE.test();
Copy the code

Hangry singleton

To address the potential problem of hungry-style, we can use lazy style, which places instance initialization before starting to use. The lazy loading code for Kotlin’s version is as follows

class LazySingleton private constructor() {companion object {
        val instance: LazySingleton by lazy { LazySingleton() }
    }
}
Copy the code
  • Explicitly declare the constructor to be private
  • Companion Objects are used to declare an object within a class
  • Instance of LazySingleton implements lazy loading through lazy
  • Lazy is thread-safe by default, which avoids the problem of multiple threads accessing simultaneously to generate multiple instances

About the lazy man’s choice

When it comes to choosing between hungry-style and slacker style, there are usually two things to consider

  • Instance initialization performance and resource footprint
  • Efficiency and brevity of writing

For instances that take less time to initialize and have a lower memory footprint, object loading should be used. Otherwise, use lazy pose.

Thread-safe slob style

We all know that there are thread-safety issues with lazy locking, and that you need to use Synchronized locks. In Kotlin, if you want to declare a method as Synchronized, you need to add @synchronized annotations.

/ / Java implementation
public class SingletonDemo {
    private static SingletonDemo instance;
    private SingletonDemo(){}
    public static synchronized SingletonDemo getInstance(){// Use a synchronization lock
        if(instance==null){
            instance=new SingletonDemo();
        }
        returninstance; }}/ / Kotlin implementation
class SingletonDemo private constructor() {
    companion object {
        private var instance: SingletonDemo? = null
            get() {
                if (field == null) {
                    field = SingletonDemo()
                }
                return field
            }
        @Synchronized
        fun get(a): SingletonDemo{
            returninstance!! }}}Copy the code

Singleton Double Check lock

/ / Java implementation
public class SingletonDemo {
    private volatile static SingletonDemo instance;
    private SingletonDemo(){} 
    public static SingletonDemo getInstance(){
        if(instance==null){
            synchronized (SingletonDemo.class) {if(instance==null){ instance=new SingletonDemo(); }}}returninstance; }}/ / kotlin implementation
class SingletonDemo private constructor() {
    companion object {
        val instance: SingletonDemo by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
        SingletonDemo() }
    }
}
Copy the code

How to add an attribute to a singleton in Kotlin’s Double Check?

class SingletonDemo private constructor(private val property: Int) {// This can be changed according to actual requirements
  
    companion object {
        @Volatile private var instance: SingletonDemo? = null
        fun getInstance(property: Int)= instance ? : synchronized(this) { instance ? : SingletonDemo(property).also { instance = it } } } }Copy the code

Static inner class

/ / Java implementation
public class SingletonDemo {
    private static class SingletonHolder{
        private static SingletonDemo instance=new SingletonDemo();
    }
    private SingletonDemo(){
        System.out.println("Singleton has loaded");
    }
    public static SingletonDemo getInstance(){
        returnSingletonHolder.instance; }}/ / kotlin implementation
class SingletonDemo private constructor() {
    companion object {
        val instance = SingletonHolder.holder
    }

    private object SingletonHolder {
        val holder= SingletonDemo()
    }

}
Copy the code

Inner class

Inner classes are represented by the inner keyword.

The inner class has a reference to the object of the outer class, so the inner class can access the attributes and functions of the member of the outer class.

class Outer {
    private val bar: Int = 1
    var v = "Member Attributes"
    /** nested inner class **/
    inner class Inner {
        fun foo(a) = bar  // Access the external class member
        fun innerTest(a) {
            var o = this@Outer // Get the member variables of the external class
            println("Inner classes can refer to members of outer classes, for example:" + o.v)
        }
    }
}

fun main(args: Array<String>) {
    val demo = Outer().Inner().foo()
    println(demo) / / 1
    val demo2 = Outer().Inner().innerTest()   
    println(demo2)   // An inner class can refer to a member of an outer class, such as a member attribute
}
Copy the code

Anonymous inner class

The format of an Object expression is: Object[: several parent types separated by commas]

Use object expressions to create anonymous inner classes:

class Test {
    var v = "Member Attributes"

    fun setInterFace(test: TestInterFace) {
        test.test()
    }
}

/** * defines the interface */
interface TestInterFace {
    fun test(a)
}

fun main(args: Array<String>) {
    var test = Test()

    /** * Uses object expressions to create interface objects, which are instances of anonymous inner classes. * /
    test.setInterFace(object : TestInterFace {
        override fun test(a) {
            println("Object expression creates instance of anonymous inner class")}}}Copy the code

Nested classes

Nested classes and inner classes are not the same thing

We can nest classes in other classes

class Outer {                  / / outside class
    private val bar: Int = 1
    class Nested {             / / nested classes
        fun foo(a) = 2}}fun main(args: Array<String>) {
    val demo = Outer.Nested().foo() // Call format: external class. Nested classes. Nested class methods/attributes
    println(demo)    / / = = 2
}
Copy the code

Data class

Kotlin can create a class that contains only data with the keyword data:

Example:

data class User(val name: String, val age: Int)
Copy the code

The data class must meet the following conditions:

  • The main constructor takes at least one argument.
  • All primary constructors must have arguments identified asvalorvar ;
  • Data classes cannot be declared asabstract.open.sealedorinner;
  • Data classes cannot inherit from other classes (but can implement interfaces).

copy()

Copy uses the copy() function, which we can use to copy objects and modify some of their properties

Copy the User data class using the copy class and modify the age property:

data class User(val name: String, val age: Int)


fun main(args: Array<String>) {
    val jack = User(name = "Jack", age = 1)
    val olderJack = jack.copy(age = 2Println (jack) println(olderJack)} Output: User(name= jack, age=1)
User(name=Jack, age=2)

Copy the code

Data classes and deconstruction declarations

Component functions allow data classes to be used in destructuring declarations:

val jane = User("Jane".35)
val (name, age) = jane // Component functions allow data classes to be used in destructuring declarations
println("$name.$age years of age") // prints "Jane, 35 years of age"
Copy the code

Standard data class

The standard library provides Pair and Triple. In most cases, naming data classes is a better design choice because the code is more readable and provides meaningful names and attributes.

Enum class enum

  • The most basic use of enumeration classes is to implement a type-safe enumeration.

  • Enumeration constants are separated by commas and each enumeration constant is an object.

enum class Color{
    RED,BLACK,BLUE,GREEN,WHITE
}
Copy the code

Enumeration initialization

Each enumeration is an instance of an enumeration class that can be initialized:

enum class Color(val rgb: Int) {
    RED(0xFF0000),
    GREEN(0x00FF00),
    BLUE(0x0000FF)}Copy the code

. The default name is the enumeration character name, and the value starts at 0. If you need to specify a value, you can use its constructor:

enum class Shape(value:Int){
    ovel(100),
    rectangle(200)}Copy the code

Enumerations also allow you to declare your own anonymous classes and corresponding methods, as well as methods that override base classes.

enum class ProtocolState {
    WAITING {
        override fun signal(a) = TALKING
    },

    TALKING {
        override fun signal(a) = WAITING
    };

    abstract fun signal(a): ProtocolState
}
Copy the code

If an enumeration class defines any members, separate the enumeration constant definitions in the member definition with a semicolon.

Using enumerated constants

Enumeration classes in Kotlin have synthetic methods that allow you to iterate over defined enumeration constants and get enumeration constants by their names.

Let’s look at the valueOf and VALUES methods

EnumClass.valueOf(value: String): EnumClass  // The conversion specifies name as an enumeration value. If the match fails, IllegalArgumentException will be thrown
EnumClass.values(): Array<EnumClass>        // Returns an enumerated value in the form of an array
Copy the code

To get enumeration information:

val name: String // Get the enumeration name
val ordinal: Int // Gets the order in which enumerated values are defined in all enumerated arrays
Copy the code

Look at an example, and you’ll make it clear

enum class Color{
    RED,BLACK,BLUE,GREEN,WHITE
}
fun main(a) {
    var color:Color=Color.BLUE

    println(Color.values().joinToString ())
    println(Color.valueOf("RED") println(color.name) println(color.ordinal)} output: RED, BLACK, BLUE, GREEN, WHITE RED BLUE2
Copy the code

14, Sealed class

The characteristics of

  • Sealed classes are used to represent a restricted class inheritance structure: when a value can have a finite number of types and no other types.

  • Declare a sealed class, using the sealed modifier. The sealed class can have subclasses, but all subclasses must be embedded within the sealed class.

    • In a sense, they are extensions of enumerated classes. The collection of values of enumerated types is also limited, butThere is only one instance of each enumerated constantAnd theA subclass of a sealed class can have multiple instances of containable state.
  • Sealed cannot modify interface,abstract (will report warning, but will not compile errors)

To some extent, the Sealed class is an enhanced enumeration class. Enumerations care more about data, and Sealed cares more about type

How about some examples

Example 1

sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()

fun eval(expr: Expr): Double = when (expr) {
    is Const -> expr.number
    is Sum -> eval(expr.e1) + eval(expr.e2)
    NotANumber -> Double.NaN
}
Copy the code

The key benefit of using a sealed class is that when you use a WHEN expression, you don’t need to add an else clause to the statement if you can verify that the statement covers all cases.

fun eval(expr: Expr): Double = when(expr) {
    is Expr.Const -> expr.number
    is Expr.Sum -> eval(expr.e1) + eval(expr.e2)
    Expr.NotANumber -> Double.NaN
    // The 'else' clause is no longer needed because we have covered all cases
}
Copy the code

Example 2

// The enclosing class cannot be instantiated externally
// External can only instantiate its subclasses
sealed class Color {
    // You can only inherit sealed classes internally
    class Red(val value: Int) : Color()
    class Green(val value: Int) : Color()
    class Blue(val name: String) : Color()
}

fun isInstance(color: Color) {
    when (color) {
        // All conditions must be written, otherwise an error is reported
        is Color.Red -> println("This is red.")
        is Color.Green -> println("This is green.")
        is Color.Blue -> println("This is blue.")}}fun main(a) {
    isInstance(Color.Red(123))} Output: This is redCopy the code

Inline class inline class

  • Kotlin introduced a special class called an inline class, which is declared by defining an inline modifier in front of the class.

  • An inline class must have a unique attribute initialized in the main constructor. At run time, this unique attribute is used to represent instances of the inline class. This is the main feature of inline classes, where the data of a class is “inlined” to where the class is used.

  • The only purpose of an inline class is to be a wrapper of some kind (similar to Java boxing types Integer, Double)

Notes for inline classes

  • At most one argument (type unlimited) (Inline classes must have a unique attribute in the main constructor)
  • No background fields (no direct assignment)
  • The wrapped class cannot be generic
  • No init block
  • Cannot inherit from or be inherited from other classes
  • Inherits from the interface, with properties and methods

example

inline class BoxInt(val value: Int): Comparable<Int> {
    override fun compareTo(other: Int)
            = value.compareTo(other)

    operator fun inc(): BoxInt {
        return BoxInt(value + 1)
    }
}
Copy the code

A Typealias looks similar to an inline class, but a Typealias simply provides an optional name for an existing type, whereas an inline class creates a new type.


Reference: www.cnblogs.com/nicolas2019… www.jianshu.com/p/ff6f5101e… www.jianshu.com/p/c9bd56a64… Droidyue.com/blog/2017/0… www.jianshu.com/p/5797b3d0e…