Class constructor

Class constructors are written as follows:

//age:Int is only visible in the constructor (including the init block). Class Person(var name:String,age:Int) class Person(var name:String,age:Int){var age:Int init{ this.age = age } }Copy the code

Class, observe the following code:

Class Person(var name:String,var age:Int):Animal(){class Person(String, age:Int):Animal(){ Constructor (age:Int):this("Unknown",age){constructor(age:Int):this("Unknown",age){constructor(age:Int):this("Unknown",age){Copy the code

The primary constructor defaults to:

Abstract Class Animal Class person@jvmoverloads This annotation enables the main constructor default argument to be called overloaded from Java code (var name:String =) "Unknown",var age:Int) :Animal(){Copy the code

Do not define a primary constructor

class Person:Animal{
    var name:String
    var age:Int
    constructor(name:String,age:Int){
        this.name = name
        this.age = age
    }
}
Copy the code

This is similar to Java, but Kotlin doesn’t recommend it because of the complexity of multiple constructors creating multiple construction paths, but Kotlin allows it for compatibility with Java features.

Class and member visibility types

Public: Like Java, is publicly visible and modifies classes, members, and top-level declarations.

Internal: Visible within a module and modifies classes, members, and top-level declarations.

Protected: Visible within classes and subclasses, decorates members.

Private: Visible within a class or file and modifies classes, members, and top-level declarations.

Lazy initialization of class attributes

Reasons for delayed initialization:

Class attributes must be initialized at construction time.

Some members will not be initialized until after class construction. If we do not delay initialization, we will have to declare the property as null, which obviously makes the code structure very bad, at least in many cases we will need to constantly nullate.

Lateinit keyword

1. Lateinit causes the compiler to ignore initialization of variables and does not support basic types such as ints.

2. Developers must be able to use LateInit with a fully determined lifetime of variable values;

Don’t use LateInit for complex logic, it makes your code more fragile.

Use case: In Android development we often declare a control variable and initialize it in onCreate

// If we do not use lateInit, we need to declare textView as a controllable type. Override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.act_main) textView = findViewById(R.id.textView) }Copy the code

The lazy mode delays initialization

Private val textView by lazy {findViewById< textView >(r.id.extView)} Override fun is executed only when textView is accessed for the first time onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.act_main) textView.text = "Hello Kotlin" }Copy the code

This approach to initialization and declaration of cohesion, without declaring nullable types, is the most popular approach to lazy initialization.

Delegate

What is an agent?

Agent is A to deal with C event instead of B, colloquial is I to deal with it instead of you, subject is I, you, it.

Let’s start with the interface proxy: The interface proxy is the way that object X implements interface B instead of the current class A. Look at the following code:

Interface Api {fun a() fun b() fun c()} class ApiWrapper(val Api :Api) :Api by Api {//Api by Api Override fun c() {// There is no need to implement a() and b() println("Hello Kotlin") api.c() // The only requirement for the object API is to implement the propped interface }}Copy the code

The property broker

The property broker simply implements the getValue and setValue methods.

Let’s look at property brokering via lazy:

fun main() { val person = Person("zhang san") println(person.firstName) } class Person(val name:String){ val firstName By lazy {name.split(" ")[0]}Copy the code

We can delegate attributes through lazy because lazy is a function that returns a lazy object:

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

The lazy object is itself a proxy, and it also implements the getValue method, so an instance of the interface lazy proxies the getter for the firstName property of the instance of the object Person:

public inline operator fun <T> Lazy<T>.getValue(thisRef: Any? , property: KProperty<*>): T = valueCopy the code

Let’s take a look at this example.

ThisRef is the object on which the property resides. ThisRef is the property. Operator fun getValue(thisRef: Any? , property: KProperty<*>):String{//String is the type of the property. Return "getValue ${property.name}"} // When identifying the proxy property class thisRef: Person :Perosn operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {println(" he is ${value} company ")} class Person{var company:String by Delegate()} fun main() {val Person = Person() person.company = "Google" println(person.company)} This is Google's getValue companyCopy the code

Person.com pany = “Google”, so setValue is called.

Observable property broker

Look at the following code:

class StateManager{ var state:Int by Delegates.observable(0){ kProperty: KProperty<*>, oldValue: Int, newValue: Int -> println("State change from $oldValue to $newValue") } } fun main() { val stateManager = StateManager() Statemanager. state = 9} State change from 0 to 9Copy the code

Delegates. Observable (0) actually creates another object of type ObservableProperty

and implements ReadWriteProperty

interface gets the getValue and setValue methods. Each time a property is set, it executes the contents of the LAMBA expression to get which property it is and the changed value.
?>

Five, the singleton

The implementation of the Kotlin singleton uses the object keyword

object Singleton{
    
}
Copy the code

When you define a Singleton, the class load instantiates the object at the same time, so the Singleton is both the name of the class and the name of the object.

@jvmField var x:Int = 2 fun b(){} @jvmfield var x:Int = 2 fun b(){}  } fun main() { Singleton.a Singleton.b() }Copy the code

The @jVMstatic annotation is used to generate static members in Kotlin. This has no effect on Kotlin. Java calls to members of an Object can be made directly as if they were static members. Note that the @jVMStatic and @jVMField annotations are only available on the Java platform.

Static member Companion Objects of ordinary classes

A Companion object is a companion object within a class with the same name as the ordinary class

Class Foo{companion object @jvmfield var a = 1 @jvmstatic fun xFun(){}}Copy the code

The class and method are written equivalent to the Java implementation below

public class Foo {
    public static void xFun(){}
}
Copy the code

Object cannot define a constructor, but can implement init blocks, class inheritance, and interfaces.

Inner class

Static and non-static inner classes in Kotlin are declared differently from Java. Inner classes created by default in Kotlin are static, and non-static inner classes decorated with the inner keyword

Class StaticInner{} class StaticInner{}Copy the code

Instantiation of an inner class

Fun main(){val inner = Outer().inner (); Val staticInner = outer.staticinner ()}Copy the code

Internal object

Class OuterObject{// The inner object does not have a non-static state, so it cannot be modified with inner. Fun main(){// Inner object val StaticInnerObject = OuterObject.StaticInnerObject }Copy the code

Anonymous inner class: An anonymous inner class is an inner class without a name, usually written in the format: object: class or interface. In actual Android development, we use anonymous inner classes when we listen to the clickworld of the control. Look at the following code:

textView.setOnClickListener(object: View.OnClickListener{
    override fun onClick(p0: View?) {
        TODO("Not yet implemented")
    }

})
Copy the code

The general form of an anonymous inner class is as follows:

Object: a Runnable, Cloneable {/ / an anonymous inner class can inherit the parent class or implement multiple interfaces override fun run () {}}Copy the code

If an anonymous inner class is defined in a static method, a static inner class, or a top-level function, you don’t have to worry about memory leaks.

Data class

The main constructor of a data class must have at least one parameter, all of which must be marked as var or val. In addition, the data class cannot be abstract. Open or internal. How to construct a data class

data class Book(
    val id: Long,
    val bookName: String,
    val author: Person
)

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

Here we see that the structure of the Book is very similar to that of the JavaBean we defined:

public class Book { public long id; public String name; public Person person; public static class Person{ public String name; public int age; }}Copy the code

However, the two are not equivalent, which means that Kotlin’s data class cannot be used directly as a JavaBean. Why is that?

The reason is that the properties defined in the main constructor of the Book are called Components. all properties are implemented based on componentN(), and each property has a corresponding componentN() method. Component1 (), Component2 (), and Component3 () These methods are the compiler generated for us, in fact, it also automatically generated equals/hashCode toString/copy method), while the component is not the custom Getter or Setter, so it cannot directly when javabeans in use. So what does this componentN() method do?

ComponentN () componentN() componentN() componentN() componentN() componentN()

Fun main(){val book = book (1234,"Kotlin start and master ", Person("JetBrains",25)) // Suppose I only want the variables at positions 1 and 3. I just change val(bookId,_, Person) = book val(bookId,bookName) = book Println (" ${bookId} bookName is $bookName")Copy the code

Component1 (bookId,bookName) = bookName val(bookId,bookName) = book

The inheritance relation of data class is that it can only inherit others, but not be inherited. If you want to achieve inheritance and use it as a JavaBean form, you need to use the plug-in of compiler to realize the no-parameter constructor of data class and remove the final keyword after compilation.

NoArg and AllOpen plugins

The NoArg plugin solves the problem of Kotlin converting to Java without a no-argument constructor, which is generated by compilation and cannot be accessed in the code before compilation.

The AllOpen plug-in addresses the problem of final removal of Kotlin code after it is converted to Java, so that the data class can be inherited to implement its getter/setter methods, thereby implementing some of the required business logic.

Use NoArg and AllOpen plugins:

Add dependencies to build.gradle under Android project:

plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' id 'org.jetbrains.kotlin.plugin.noarg' version '1.3.60' id '. Org. Jetbrains kotlin. Plugin. Allopen 'version' 1.3.60} noArg {/ / true executes the init block of code to false by default invokeInitializers = true annotations "com.qisan.kotlinstu.Poko" } allOpen { annotations "com.qisan.kotlinstu.Poko" }Copy the code

Define a comment:

package com.qisan.kotlinstu

/**
 * Created by QiSan
 * package com.qisan.kotlinstu
 */
annotation class Poko
Copy the code

Annotate the data class:

@Poko data class Book( val id: Long, val bookName: String, val author: Person) {init {println(" I'm init in Book ")}} @poko class Person(val name: String, val age: Int) fun main () {/ / by reflection for Book instance val Book = the Book: : class. Java. NewInstance ()} printing results: I am a data class Book of initCopy the code

Some final modifications have been removed. The compontentN() and copy methods are still final, but the data classes can be inherited by now:

public class Book { private final long id; @NotNull private final String bookName; @NotNull private final Person author; public long getId() { return this.id; } @NotNull public String getBookName() { return this.bookName; } @NotNull public Person getAuthor() { return this.author; } public Book(long id, @NotNull String bookName, @NotNull Person author) { Intrinsics.checkNotNullParameter(bookName, "bookName"); Intrinsics.checkNotNullParameter(author, "author"); super(); this.id = id; this.bookName = bookName; this.author = author; String var5 = "I'm init in data class Book "; System.out.println(var5); }}Copy the code

Enumeration classes

Enumeration class declaration in Kotlin:

Enum class State{idle,Busy} fun main(){// Println (" Enumeration name ${state.idle. name}") println(" enumeration number ${state.busy. Ordinal}")} Result: Enumeration name idle Enumeration number 1Copy the code

Enumeration class defines the constructor:

Enum class State(val ID :Int){idle(0),Busy(1)}Copy the code

Enumeration classes implement interfaces:

enum class State : Runnable {
    idle, Busy;
    override fun run() {
        TODO("Not yet implemented")
    }
}
Copy the code

It is also possible for each enumeration implementation interface method to perform different operations:

enum class State : Runnable { idle{ override fun run() { TODO("Not yet implemented") } },Busy{ override fun run() { TODO("Not yet Implemented ")}}} Fun main() {// Call the implemented method state.idle.run ()}Copy the code

It is important to note that the parent class of enumeration is Enum, so it cannot inherit from other classes.

Conditional branches of enumeration classes:

val state:State = State.idle

val value = when(state){
    State.idle -> {0}
    State.Busy -> {1}
}
Copy the code

Range of enumeration:

enum class Color{ White,Red,Green,Blue,Yellow,Black } fun main() { val colorRange = Color.White .. Color.Yellow val colorValue = color. Green println("Green in colorRange:${colorValue in colorRange}")}  Green in colorRange:trueCopy the code

Nine, sealing class

Concept of sealed class:

A sealed class is a special abstract class. It is an abstract class first and then a sealed class, and its subclasses must be defined in the same file as themselves. The number of subclasses of sealed classes is limited, and in a sense, they are extensions of enumerated classes. To declare a sealed class, use the sealed modifier on the class name.

Sealed cannot modify interface abstract class. (Sealed cannot modify interface abstract class)

Observe the following code:

// Sealed class Expr Const Sum NotANumber data class Const(val number: Double) : Data class Sum(val e1: Expr, val e2: Expr) : Expr() object NotANumber: Expr() Case statement Fun eval(expr: expr): Double = when (expr) { is Const -> expr.number is Sum -> eval(expr.e1) + eval(expr.e2) NotANumber -> Double.NaN } fun Main () {val c1 = Const(2.0) val c2 = Const(5.0) println("c1 + c2 = ${eval(Sum(c1,c2))}")} c1 + c2 = 7.0Copy the code

10. Inline classes

The class modified by the inline keyword is an inline class.

The concept of inline classes:

An inline class is a wrapper around a class, similar to a Java boxing type, and the compiler optimizes the wrapped type whenever possible. How do you optimize it? Sometimes, we define a class, there is only one properties and operating of the method, we can define it as an inline type, the compiled static methods that can be produced by the name of the class points method can call, don’t have to create a class instance, this will not increase the object storage to the JVM heap, and storage and use of object instances can have performance loss, Although individually small, projects can add up to make a big difference to the quality of the code, so the inline class saves on the overhead of creating objects for the class.

The inline class must have a unique attribute initialized in the main constructor, and the attribute needs to be decorated with val. At run time, this unique attribute is used to represent instances of the inline class. An inline class may implement an interface, but it cannot inherit or be inherited from its parent class.

Example of compiler optimization for inline class:

Inline class PlayerState(val state: String) { fun setPlayState() = when (state) { "stop" -> { println("play stop") 0 } "prepare" -> { println("play prepare") 1 } "play" -> { println("play start") 2 } "onPuase" -> { println("play onPuase") 3 } else -> { println("play Error ") -1}}} fun main() {val state = PlayerState("play") println(state.setplayState ())} Play start 2Copy the code

If we look at the bytecode compiled to Java and run, we can see:

public final class InlineClassKt { public static final void main() { String state = PlayerState.constructor-impl("play"); int var1 = PlayerState.setPlayState-impl(state); System.out.println(var1); }}Copy the code

The compiler generates static methods similar to those found in Java, so no heap objects are generated throughout the call process because the methods are called directly from the PlayerState class, saving memory for object creation.