Android - Kotiln basics tutorial

preface

In the previous article, we mainly explained Kotlin's knowledge points related to sets. In this article, we will explain Kotlin's objects in detail!

1. Object

1.1 example 1 (no constructor)

class Player {
	//Analysis point 1
    var name = "abc"
        get() = field.capitalize()
        set(value) {
            field = value.trim()
        }
    var age = 10  
        get() = field.absoluteValue
        private set(value) { 
            field = value.absoluteValue
        }
    val rolledValue
    	//Take random number
        get() = (1 until 6).shuffled().first()

    var words: String? = null //Analysis point 2

    fun saySomeThine() { 
    	//Analysis point 3
        words?.also { "Hello ${it.toUpperCase()}" }.run(::println)
    }
}
fun main{
    var player = Player()
    player.name = "tom"
    //player.age=-10 / / an error is reported here because set is changed to private
    player.words="main hello" 
    println(player.name)
    println(player.age)
    println(player.rolledValue)
    player.saySomeThine()
}

Defines an object with corresponding member properties. Pay attention to the analysis points:

  • Analysis point 1: the corresponding get and set methods are implemented respectively, and the corresponding logic is implemented in the corresponding methods.
  • Analysis point 2: this member variable does not have any get or set methods
  • Analysis point 3: in the method saySomeThine, the. also keyword is used for the member attribute, and then printed

Operation effect:

Tom
10
4
main hello

From this operation effect, we can see that even if the members in the object do not implement the corresponding get and set methods, and do not use private, they can still call the corresponding get and set methods externally.

1.2 example 2 (with main constructor)

// ----------------Initialization
class Player1(
    _name: String,
    _age: Int,
    _isNormal: Boolean) {
    var name = _name
        get() = field.toLowerCase()
        private set(value) {
            field = value.trim()
        }
    private var age = _age
    var isNormal = _isNormal

    fun playerInfo() {
    	//println(_name) / / an error will be reported
        println("name: $name; age:$age ;isNormal $isNormal")
    }
}

fun main() {
    var player = Player1("Tom", 20, true)
    player.playerInfo()

}

Operation effect

name: tom; age:20 ;isNormal true

From this code, Player1 should be an object with a parameter constructor. And the corresponding_ name,_ age,_ isNormal is the construction parameter in the corresponding construction method, and the validity period is only in the corresponding construction method.

1.3 example 3 (with primary and secondary constructors)

class Player2(
    _name: String,
    var age: Int = 12, //Execution sequence 1
    var isNormal: Boolean) {
    var name = _name //Execution sequence 2
        get() = field.capitalize()
        private set(value) {
            field = value.trim()
        }
    fun playerInfo() {
        println("name: $name; age:$age ;isNormal $isNormal")
    }
    //Secondary constructor [] this() here is equivalent to executing the primary constructor, equivalent to order 1 and 2
    constructor(name: String) : this(name, age = 100, isNormal = false ) {
        this.name = name.toUpperCase()
        println("constructor----------")
        //Execution sequence 4
    }
    init { // Execution sequence 3
        //The initialization code block is executed when the class instance is constructed
        // You can check the value here, and throw an exception if the condition is met
        println("init----------")
        require(age > 10) { println("age must be positive") }
        require(name.isNotBlank()) { println("player must have a name") }
    }
}

fun main() {
    var player2=Player2("Tom", 20, true)
    player2.playerInfo()
    var player3 = Player2("bob", isNormal = false)
    player3.playerInfo()
    var player4=Player2("hqk")
    player4.playerInfo()
}

Operation effect:

init----------
name: Tom; age:15 ;isNormal true
init----------
name: Bob; age:12 ;isNormal false
init----------
constructor----------
name: HQK; age:100 ;isNormal false

This time, constructor and init are added to the code. Constructor means to create a secondary constructor; Init is equivalent to static code block in java. The corresponding execution sequence is clearly marked in the notes.

The init code block contains the require method, which means that when the expression in it does not hold / false, an error will be reported directly, and the user-defined error information will be implemented in the following closure.

1.4 example 4 (delayed initialization)

class Player3 {
    lateinit var equipment: String
    fun ready() {
        equipment = "sharp knife"
    }

    fun battle() {
        if (::equipment.isInitialized) println(equipment)
    }
}

fun main{
    var palyer3 = Player3()
    palyer3.ready()   //This method must be called to use the corresponding property variable
    palyer3.battle()
}

Operation effect:

sharp knife

In the previous study, we realized that when we define variables, we either assign initial values at the beginning or pass? Indicates that this variable is nullable. However, in the use of objects, I don't want to assign initial values at the beginning, nor do I want this variable to be nullable, so I can only use lateinit to indicate that this attribute variable needs to be initialized later (before using it)!

What if it is not initialized before using it? Isn't it just a null pointer exception? Kotlin also provides a corresponding solution: as long as you can't confirm whether the lateinit variable is initialized, you can perform the isInitialized check

1.5 example 5 (inert initialization)

class Player4(_name: String) {
    var name = _name
    val config by lazy { loadConfig() }
    private fun loadConfig(): Boolean {
        println("loading...")
        return false
    }
}

fun main{
    var play4 = Player4("Tom");
    println(play4.config)
}

Operation effect

loading...
false

Here we can see that when defining the member attribute, the method of by lazy {} is used to make config implement lazy initialization. When the play4.config member property is used, it initializes the corresponding operation.

1.6 example 6 (initialization trap)

class Player5() {
    init {
    	//This will directly report an error
        blood  = this.blood.times(4)
    }
    var blood = 100
}

When we put the variable after the init closure, we will find that the code actually reports a syntax error. The solution is also simple, just put the init closure at the end.

class Player6() {
    var blood = 100
    init {
    	//So you won't report an error
        blood  = this.blood.times(4)
    }
}

fun main{
   var play6=Player6()
   println(play6.blood )
}

Operation effect:

400

1.7 Example 7 (inheritance of objects)

class Player7() {
    val name: String

    private fun firstLetter() = name[0]

    init {
        name = "Jack" //Analysis point 1
        firstLetter()
    }
    //--------------Inherit----------------
    open class Product(val name: String) {	//Analysis point 2
       open fun description() = "Product: $name"
        open fun load() = "Nothing..."
         fun add(num1: Int, num2: Int) = num1 + num2 //This method cannot be overridden by subclasses
    }
    class LuxuryProduct : Product("Java") {
        //---Heavy load
        override fun load() = "Java " + description()
    }
}
/**
 * Type conversion
 */
fun sale(p: Player7.Product) {
    println(p.load())
}

fun main() {
    val play7 = Player7()
    println(play7.name)

	val product = LuxuryProduct()
    product.description().run(::println)
    product.load().run(::println)
    //Type detection
    println(product is LuxuryProduct)
    println(product is Player7.Product)

    sale(product)
    sale(product as Player7.Product) //Analysis point 3
}

Let's look at these analysis points first:

  • Analysis point 1: as in the above example, when the init code block is under the corresponding attribute definition, the corresponding variable can be initialized in this closure. Even if the variable is decorated with val, it can still be initialized.
  • Analysis point 2: when we want to use inheritance, we can define the inherited object through open class XXX. If the methods inside want to be rewritten by sub objects, we can use open to modify the corresponding methods. The syntax of subclass inheriting parent class is: class subclass: parent class
  • Analysis point 3: when we want to convert an object, we can use the as keyword to force the corresponding object.

Operation effect

Jack
Product: Java
Java Product: Java
true
true
Java Product: Java
Java Product: Java

Conclusion

Well, that's the end of this article. I wanted to finish talking about the object in this article. However, I found that the content was too rich. There are many, many, and time is not allowed. I have to write that code, so I'll put it in the next article for the time being.

Keywords: Android kotlin

Added by ScottCFR on Fri, 12 Nov 2021 09:23:33 +0200