kotlin - classes and objects

kotlin - classes and objects

Properties of class

Class attribute is actually used to save the state data of the class object. If no modifier is written in kotlin, the access permission of this attribute is public by default. Class does not require us to write setter and getter methods, except when customization is required.

class Person {
    var name: String = ""
    var age: Int = 0
    lateinit var address:String
}

kotlin stipulates that the attributes of a class must have initializers. In short, they must have initialization values. If you don't want the attributes to be initialized as soon as they come, you can use the keyword lateinit. The keyword lateinit indicates delayed initialization.

lateInit decorated attribute

lateInit is used in kotlin to solve the delayed initialization of attributes. Using the key modified attributes of lateInit, you can not specify the initialization value when defining the attribute or in the constructor.

Conditions for using lateInit

  • lateInit can only modify variable attributes declared in the class body (not using val or attributes declared in the main construct)
  • The attribute modified by lateInit cannot customize getter and setter methods.
  • The attribute of lateInit modifier must be a non empty type.
  • The attribute modified by lateInit cannot be a basic type (the eight basic types corresponding to java).
    Note: the attribute modified by lateInit cannot be accessed directly in use because no initialization value is specified in the definition and construction method. Confirm whether it has been initialized before use.

Inline attribute

The inline keyword can modify the attribute without a field behind the scenes. You can see when there is no field behind the scenes Behind the scenes fields and attributes This article. inline can directly modify this property (equivalent to modifying getter and setter methods at the same time) or modify any of getter and setter.
The example code is as follows:

class Name(name:String,desc:String){
    var name=name
    var desc=desc
}

class Product{
    var productor:String?=null
    //inline modifies the getter method, indicating that it will be inlined when reading
    val proName:Name
    inline get()= Name("guojingbu","excellent!!")
    //inline modifies the setter method, indicating that it will be inlined when the setting is worth it
    var author:Name
    get() = Name("guojingbu","")
    inline set(value) {
        this.productor=value.name
    }
    //Inline directly modifies attributes, indicating that both reading and setting attribute values are inline.
    inline var pubHouse:Name
    get() = Name("China People's Publishing House","nothing")
    set(value) {
        this.productor=value.name
    }
}

Class construction method

A class in Kotlin can have 0 ~ 1 primary constructors and one or more secondary constructors.

primary constructor

The primary constructor is part of the class header: it follows the class name (and type parameters)

class Person constructor(name:String,age:Int) {}

If the main constructor does not have any annotations or visibility modifiers, the constructor keyword can be omitted.

class Person (name:String,age:Int){}

Although the main constructor cannot define the execution body, it can define some formal parameters, which can be used in attribute declaration and initialization block. The following code:

class Person (a:String,b:Int) {
    var name: String =a
    var age: Int = b
    init {
        this.name=a
        this.age=b
    }
}

Initialization block

The initialization block in kotlin is identified by the init keyword. There can be multiple initialization blocks, which are executed according to their occurrence order during instance initialization. The code is as follows:

class Person (name:String) {
    val firstProperty="firstProperty = $name".also(::println)
    init {
       println("I'm initializer 1")
    }
    val secondProperty="secondProperty= ${name}.length".also(::println)
    init {
       println("I'm initializer 2")
    }
}

The result printed by the above code is as follows:

firstProperty = guojingbu
 I'm initializer 1
secondProperty= 9
 I'm initializer 2

From the print results, we can see that the initialization of attributes and initialization blocks are executed in order. We cannot initialize the attributes declared later in the previous initialization block.

Secondary constructor

  • The secondary constructor is also implemented through the constructor. The code is as follows:
class Person3{
    var name: String=""
    constructor(name: String){
        this.name = name
    }
}
  • If the primary constructor is customized, the secondary constructor must call the primary constructor directly or indirectly. The code is as follows:
class Person3(){
    var name: String=""
    var age:Int=0
    constructor(name: String):this(){
        this.name = name
    }
    constructor(name: String, age: Int) : this(name) {}
}

**The initialization of class attributes and class initializers are part of the main constructor, * * so they will be executed before the secondary construction. The code is as follows:

class Person3(){
    var name: String="Property is initialized----".also(::println)
    constructor(name: String):this(){
        println("The second construct was executed----")
        this.name = name
    }
    init {
        println("Initializer executed----")
    }
}

The printing results are as follows:

Property is initialized----
Initializer executed----
The second construct was executed----

inherit

All classes in kotlin do not specify a superclass. The default superclass is Any. The code is as follows:

fun main(args:Array<String>){
    println("Example is Any = ${Example() is Any}")
}

class Example

Output:

Example is Any = true

The classes in Kotlin are final by default. If you want to be inherited, you have to modify them with the "open" keyword.

open class Animal{}
class Dog: Animal() {
}

All constructors of a subclass must directly or indirectly call the constructor of a parent class

open class Animal {
    constructor(name: String) {
    }
}
class Dog : Animal {
    constructor(name: String) : super(name) {
    }
    constructor(name: String,age: Int):this(name){
    }
}

Method coverage

Methods that can be overridden by subclasses in kotlin must be modified with the open keyword, and the code is as follows:

open class Shape {
    open fun draw() {
    }
}
class Circle() : Shape() {
    override fun draw() {   
    }
}

The rewriting of methods is basically consistent with the rules of java. If you don't want the methods of the parent class to be rewritten by the subclass, don't modify the methods with the open keyword. The methods and properties of the default kotlin class are final.

Override rules to follow

Coverage should follow the two same two small and one big rule:

  • Two same: the method name is the same and the method list is the same
  • Two minor: the return value type of the subclass method should be smaller or equal than that of the parent method; The exception thrown by the subclass method declaration should be smaller or the same as that thrown by the parent method declaration.
  • One: the access rights of subclass methods should be greater or equal than those of parent methods.

Property overrides

The coverage of attributes in kotlin is similar to that of class member methods. It also needs to modify attributes with the open keyword. The attribute modified by val keyword in the parent class can be overridden as var keyword modification in the child class, otherwise it cannot be modified. The code is as follows:

open class Shape {
    open val sideSize:Int=0
}
class Circle() : Shape() {
    override var sideSize:Int=1
}

Note: the access rights of subclass members can be greater than those of the parent class, but not less than those of the parent class.

Call the properties and methods of the parent class

The subclass can call the methods and properties of the parent class through the super keyword. The code is as follows:

open class Shape {
    open val name: String = "Shape"
    open fun draw() {}
}

open class Rectangle : Shape() {
    override var name: String = super.name
    override fun draw() {
        super.draw()
    }
}

The inner class can call the methods of the parent class of the outer class in the way of "super @ outer class name". Code examples are as follows:

open class Parent {
    open fun method() { println("Parent.method") }
}
class Child:Parent(){
    inner class Inner{
        fun test(){
            super@Child.method()
        }
    }
}

Override rules

When the same method appears in the inherited parent class and the implemented interface, you can use the form of super < class name > to specify which method to access. The example code is as follows:

interface Action {
    fun eat() {
        println("Action")
    }
}

open class Animal {
    open fun eat() {
        println("Animal")
    }
}

class Dog() : Animal(), Action {
    override fun eat() {
        super<Action>.eat()
        super<Animal>.eat()
    }
}

Methods and functions

Differences between top-level functions and class member methods

  • protected and abstract cannot be used in top-level functions
    And final modifiers, but the methods in the class can use public, internal, private, final, abstract and open modifiers.
  • The calling methods are different. The top-level function is through the package name. Function name (), while the method in the class is through the instance. Method name ().

Relationship between method and function

We generally call the methods defined outside the class functions and the methods defined in the class methods. Their definition syntax is the same, and they can be converted. For example, the following columns:

//Relationship between method and function
class Dog{
    fun eat(food:String){
        println("eat---I am eating: $food")
    }
    fun run(){
        println("run---Start running")
    }
}
fun main(args:Array<String>) {
    //Assign the eat method of Dog class to the variable et
    //The system will automatically infer that the type of variable et is (dog, string) - > unit
    var et=Dog::eat
    //Assign the run method of the Dog class to the variable rn
    //The type of variable rn should be (dog) - > unit
    var rn: (Dog) -> Unit =Dog::run
}

From the above examples, we can see the following points:

  • Methods that reference a class need to use class name:: method name
  • When the program separates the method of the class (run()) into a function, the caller (Dog) of the method (run()) will be passed in as the first parameter of the method.

Infix notation

Class methods decorated with infix with one parameter can be called using infix notation, similar to binocular operator.
Examples are as follows:

class ApplePack(weight:Int){
    var weight = weight
    override fun toString(): String {
        return "ApplePack:[weight = $weight]"
    }
}
class Apple(weight: Int){
    var weight=weight
    infix fun add(other:Apple):ApplePack{
        return ApplePack(this.weight+other.weight)
    }
}
//call
fun main(args:Array<String>) {
    val origin = Apple(10)
    var applePack:ApplePack = origin add Apple(5)
    println(applePack.toString())
}

Conditions for calls using infix notation:

  • Method needs to be decorated with the keyword infix
  • Method must have one parameter
  • Must be defined in a class or extension function

componentN method and Deconstruction

kotlin allows the "Deconstruction" of N attributes of an object to multiple variables, which is written as follows:

var(name,age,address)= user

The above line of code means to assign the attribute value in the user object to the name, age and address variables.
kotlin implements object deconstruction by defining the operator keyword in the class and modifying the componentN() method. The example code is as follows:

class User(name:String,age:Int,address:String){
    var name = name
    var age=age
    var address=address

    operator fun component1():String{
        return this.name
    }
    operator fun component2():Int{
        return this.age
    }
    operator fun component3():String{
        return this.address
    }
}
//use
fun main(args:Array<String>) {
    val user = User("guojingbu", 20, "Beijing")
    var(name,age,address)= user
    println("name = $name  age = $age  address= $address")
}

If we want to ignore some properties of the object, we can use "_ "Occupied", the code is as follows:

  val user = User("guojingbu", 20, "Beijing")
 	var(_,age2,address2)=user
   println(" age2 = $age2  address2= $address2")

Note: the name of the deconstruction method must be named after the component + number, otherwise an error will be reported
Map in kotlin supports deconstruction statements. For example, we can traverse the map set with the following code:

for((key,value) in map){
}

Class

The rules for importing packages in kotlin are basically the same as those in java. Kotlin's import statement supports the as keyword to take an alias for the imported class. The main purpose is to solve the problem of importing multiple classes with the same name in different packages.
Example code:

import java.util.Date
import java.sql.Date as SDate
fun main(args:Array<String>) {
    var date = Date(System.currentTimeMillis())
    var sdate =SDate(System.currentTimeMillis())
    }

Type conversion

is and! is operators

  • kotlin uses is and! Is to check the type. The type before is or! Is is is a compile time type, and the type after is is called a runtime type. The type before is must be a subsequent type or its subtype to return true.
  • After using the is keyword, the variable type before is will be automatically converted to the target type.

As and as? Operators

as: is an unsafe cast type converter. If the transformation fails, the program will report a ClassCastException exception
as?: a safe mandatory type converter. If the transformation fails, the program will not generate an exception, but directly return null.

Tags: Android kotlin

Posted on Sat, 06 Nov 2021 12:58:01 -0400 by itsgood