More articles can visit my Blog Aengus | Blog

An OverView of the OverView

  • Kotlin’s variable or method declarations are “anti-” compared to Java, meaning that the type declaration comes first and the variable or method name comes first.
  • Kotlin is stricter in that variables are determined at method declaration time and classes are not inheritable by default.
  • Kotlin is safer. Classes declared by variables are non-null, and if null is allowed, they need to be appended to the class?;
  • Kotlin withprintln()The function prints and does not add at the end of each line;;
  • The Kotlin annotation format is the same as Java;
  • Kotlin supports string template expressions, using"${expression}"The format can be correctexpressionPerforms an operation and converts it to a string;

Variables and Functions

The Kotlin variable declaration format is as follows:

val/var a: Type = someThing
Copy the code

Val stands for immutable, corresponding to the final Type in Java. Var stands for mutable variable, which is equivalent to normal variables in Java. Type is a variable Type and cannot be null. If null is allowed, Type? Said. Declare the variable val or var, which is Kotlin security. In general, immutable objects are thread-safe.

Kotlin’s general function declaration is as follows:

fun funcationName(arg: Type): ReturnType {
    / / the function body
}
Copy the code

As you can see, the parameter declaration is also a posttype, ReturnType is the ReturnType of the function, or no ReturnType can be written to represent no return value. There are many more uses of the Kotlin function, which we will cover later.

You can add the keyword private at the beginning of a variable or function declaration.

Loops, lists, and maps

Kotlin differs only slightly from Java in that the basic structure of the for loop is:

for (item: Type in collection) { / *... * / } // You can omit the type
Copy the code

If digital iteration is used, the following two methods are commonly used:

// interval, left closed and right closed. I = 1,2,3
for (i in 1.3.) { / *... * / }
// The function "until" is used to close and open. I = 1,2
for (i in 1 until 3) { / *... * / }
// You can use step and downTo to control the step size and order. I = 10,8,6,4,2
for (i in 10 downTo 2 step 2) { / *... * / }
Copy the code

Kotlin uses listOf() to generate a List and MapOf() to generate a Map as follows:

val list = listOf<String>("a"."b"."c")
print(list[1]) // b
val map = mapOf<String, Int> ("key1" to 1."key2" to 2)
print(map["key1"]) / / 1
Copy the code

More on collections later.

Everything is an object

Kotlin is more object-oriented than Java. In Kotlin, Java data types such as Boolean, int,char,double, etc. are all capitalized, i.e. Boolean, int,char, etc. This means that these data types are objects and can be manipulated using the following method:

1.toChar()
true.and(false)
1.2.compareTo(1)
Copy the code

Class Class

In Java, all objects inherit from Object, whereas in Kotlin, all objects inherit from Any (or Any? If null is possible). . The Any class has three methods: hashCode(), equals(), and toString(). Class declaration and creation methods are as follows:

class Name {
  / / class
}
// If there is no body in the class, you can use this method
class Empty
// Create class, Kotlin does not have the new keyword
val person: Person = Person()
// Kotlin has a type inference mechanism that can omit variable type declarations
val person = Person()
Copy the code

Constructor

Each class can have a primary constructor and a secondary constructor.

Principal constructor

The main constructor looks like this:

class Person constructor(firstName: String) { / *... * / }
// The constructor keyword can also be omitted if the constructor has no annotations
class Person(firstName: String) { / *... * / }
// If annotated, the constructor keyword should not be omitted
class Person public @Inject constructor(firstName: String) { / *... * / }

// Recommended
class Person(val firstName: String) { / *... * / }
/ / or
class Person(private val firstName: String) { / *... * / }
Copy the code

A primary constructor does not have a function body. If you want to initialize it (like constructors in Java), you can put it in an init block, which is called when the object is created:

class Person(firstName: String) {
    init {
        println("Constructor")}}Copy the code

Kotlin’s main constructor is a bit like syntactic sugar. When you call the constructor, the Kotlin compiler automatically assigns parameters to arguments declared in the main constructor, similar to the process in Java:

class Person {
   private String name;
  
    public Person(String name) {
        this.name = name;
    }
}
Person person = new Person("Bob");
Copy the code

Therefore, the arguments in the Kotlin primary constructor can be used anywhere in the class body. The default modifiers for classes, fields, and functions are public, protected, and private in Kotlin, but the use of internal means visible within the same Module.

If no primary constructor is specified, the compiler automatically generates a primary constructor with no arguments, as in Java.

subconstructor

The subconstructor is represented in the body class using the constructor function:

class Person {
    constructor(name: String) { / *... * /}}Copy the code

A class can have multiple secondary constructors, but only one primary constructor. If a class has both primary and secondary constructors, then each secondary constructor must call the primary constructor directly or indirectly, using the this keyword:

class Person(val name: String) {
      constructor(name: String, age: Int) : this(name) { / *... * / }
      constructor(name: String, age: Int, sex: String) : this(name, age) { / *... * /}}Copy the code

The main constructor call precedes this constructor. Note that ** only the arguments in the main constructor can be called anywhere in the class body, and the arguments in this constructor can only be used in the constructor. ** Here is a more complete example:

class Person(val name: String) {
    init {
        println("name: ${this.name}")}constructor(name: String, age: Int) : this(name) {
        println("name: $name, age: $age")}constructor(name: String, age: Int, sex: String) : this(name, age) {
        println("name: $name, age: $age, sex: $sex")}}fun main(a) {
  val person = Person("Zhang")}/ / output:
/ / name: zhang SAN
// name: zhang SAN, age: 18
// Name: Zhang SAN, age: 18, sex: male
Copy the code

The Kotlin constructor arguments support default values as follows:

class Person(val name: String = "Bill") {
    init {
        println("name: ${this.name}")}constructor(name: String = "Fifty", age: Int = 20) : this(name) {
        println("name: $name, age: $age")}constructor(name: String = "Daisy", age: Int = 21, sex: String = "Male") : this(name, age) {
        println("name: $name, age: $age, sex: $sex")}}fun main(a) {
    val person = Person()
    val person1 = Person(name="甲")
    val person2 = Person(name="乙", age=10)
    val person3 = Person(age=30)
    val person4 = Person(sex="Female")}// The first line of code output:
/ / name: li si
// The second line of code output:
/ / name: armor
// Third line of code output:
/ / name: b
// name: 乙, age: 10
// The fourth line of code output:
/ / name: fifty
// name: wangwu, age: 30
// Line 5 output:
/ / name: zhao six
// Name: zhao Liu, age: 21
// Name: zhao Liu, age: 21, sex: female
Copy the code

As you can see, Kotlin will prefer the constructor with fewer arguments, and will select the constructor with more arguments only when the argument passed is not in the constructor. Note that val/var is not allowed for arguments in the secondary constructor.

Inheritance Inheritance

As mentioned above, all classes inherit directly or indirectly from Any class. In Kotlin, all classes are final by default, because classes are not inheritable by default, and if they are allowed to be inherited by other classes, they need to be decorated with the open keyword:

open class Person { / *... * / }
Copy the code

When a parent class specifies a primary or secondary constructor: If a subclass has a primary constructor, the parent class must be initialized immediately upon inheritance:

open class Father(val lastName: String) {
    init {
        println("The father's surname${lastName}")}}class Son(val lastNameSon: String) : Father(lastNameSon) {
    init {
        println("The son surname$lastNameSon")}}Copy the code

When a parent class specifies a primary or secondary constructor: If the child class does not have a primary constructor, then each of its secondary constructors must initialize the parent class with super, or delegate to another constructor that does this:

open class Father(val lastName: String)

class Son : Father {
    constructor(familyName: String) : super(familyName) {
        println("We're using the superclass field lastName=$lastName")}// The first subconstructor delegated to a subclass
    constructor(familyName: String, age: Int) : this(familyName) {
        println("We know the father's surname by the son's surname$lastNameSon,$ageAt the age of")}}fun main(a) {
    val son = Son("The king")
    val son2 = Son("The king".20)}// The first line of code output:
// we are using the superclass field lastName= king
// The second line of code output:
// we are using the superclass field lastName= king
// From the son's surname we know the father's surname is Wang. The son is 20 years old
Copy the code

When the parent class does not specify a primary or secondary constructor: If the child class does not specify a primary constructor and a secondary constructor, the default constructor of the parent class is used when inheriting. Otherwise, use super to instantiate the parent from the primary constructor or from the secondary constructor:

open class Father

class Son : Father(a)class Son2 : Father {
    constructor() : super() { / *... * /}}Copy the code

Kotlin, like Java, supports only single inheritance, but can implement multiple interfaces.

Rewrite the override

Overriding a parent class method in Java uses the @override annotation, while overriding a parent class method in Kotlin uses the Override keyword. Besides, functions are final by default. If they need to be overridden ina subclass, they also need to be modified with the open keyword:

open class Father {
    open fun eat(a) { / *... * / }
    fun sleep(a) { / *... * /}}class Son : Father() {
    override fun eat(a) { /* .... */}}Copy the code

Using the final keyword prevents methods from being overridden again by subclasses (only by themselves) :

class Son : Father() {
    final override fun eat(a) { /* .... */}}Copy the code

Kotlin also supports attribute rewriting in a similar way to function rewriting:

open class Father() {
    open val age: Int = 40
}
class Son1(override val age: Int = 22) : Father() // age cannot be changed
class Son2 : Father() {
    override var age = 20;	// age Can be changed at this time
}
Copy the code

Abstract Class

Abstract classes in Kotlin are similar to Java, using the abstract keyword. Note that when an abstract class is inherited, the parent class (abstract class) needs to be instantiated:

abstract class Father
class Son : Father(a)Copy the code

Abstract classes are also open by default.

Interface Interface

Kotlin uses interface to declare the interface. The method in the interface defaults to open and supports default method implementation. Interfaces can also have attributes:

interface Worker {
    val count: Int  // Attributes are abstract, assignments are not allowed
    fun run(a) {
       println("Methods in Worker")}}Copy the code

Note that the implementation class overrides the attributes in the interfacevarProperties intovalProperty, but can bevalintovar.

When a subclass implements or inherits both an interface and a parent class, the override rule is as follows:

open class Machine {
    open fun run(a) {
        println("Methods in Machine")}}class Computer : Machine(), Worker {
    val app = "QQ"
    override fun run(a) {
        println("Methods in Computer")}fun runAll(a) {
        run()
        // Call the methods of the parent class or interface in this way
        super<Machine>.run()
        super<Worker>.run()
    }
}
fun main(a) {
    val com = Computer()
    com.runAll()
}
/ / output
// methods in Computer
// the method in Machine
// Methods in Worker
Copy the code

Inner Class Inner Class

A nested class is a class within a class:

class Computer {
    val app = "QQ"
    class Desktop {
        fun print(a) {
          println("Methods in nested classes")}}}Copy the code

The nested class does not have access to the methods or attributes of the outer class, but when the inner keyword is used to modify the nested class, the nested class becomes an inner class and can access the attributes or methods of the outer class. This is because the inner class contains a reference to the outer class (automatically generated by the compiler) :

open class Machine {
    open fun playOuter(a) {
        println("Superclass method")}}class Computer : Machine() {
    val app = "QQ"
    override fun playOuter(a) {
        println("Methods of external classes")
    }
    inner class Desktop {
        fun print(a) {
            println(app)
        }
        fun play(a) {
            playOuter()  // Call the external class method
            super@Computer.playOuter() // Call the method of the external class's parent class}}}fun main(a) {
    val desktop = Computer().Desktop()
    desktop.print()
    desktop.play()
}
/ / output:
// QQ
// External class methods
// Parent method
Copy the code

Data Class

When writing Javabeans in Java, we often need to override equals(),hashCode(),toString(), etc.,to make them easier to use, but these are repetitive tasks that can also be done with the @data annotation in Lombok. In Kotlin, all of this is done with the data keyword. The declaration of the data class is as follows:

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

In addition, the data class also provides a function copy() to help us copy the data class. Copy () generates its corresponding constructor for each data class. For example, the above data class generates its copy() method and call method as follows:

copy(val name: String, val age: Int)
// How to use it
val oldUser: User = User("Bill".20)
val newUser: User = oldUser.copy(name="Fifty")
Copy the code

ComponentN () returns the NTH attribute of a data class. For example, componentN() uses the following:

val user = User("Zhang".20)
val name1 = user.component1() // name1 = ""
val age1 = user.component2()	// age1 = 20
// Component1 (), Component2 () is called automatically
val (name2, age2) = user        // (name2 = "age2 ", age2 = 20)
// You can also use this method
val collection = listOf(User("a".1), User("b".2), User("3".3))
for ((name, age) in collection) {
    println("name is $name")
    println("age is $age")}Copy the code

Data classes can also have the body class, and we can also override methods like toString() in our own way. ** If you want your data class to have a constructor with no arguments, you can specify default values for all arguments. ** If you don’t want the compiler to automatically add arguments to the constructor, you can put them in the body:

data class User(val name: String) {
    var age: Int = 18
}
Copy the code

Data classes currently have the following limitations:

  • The main constructor has at least one argument;

  • Arguments in the main constructor must be decorated with val/var;

  • Final methods in the parent of a data class are not overridden;

  • Data classes cannot be abstract,open,sealed, or inner.

  • Overridden if the parent class has Open componentN() and returns a compatible type; If incompatible types are returned, an error is reported.

Sealed Class

A sealed class is visible only to classes in its file, not to classes in other files (but its subclasses are visible to classes in other files). Sealed class: sealed

sealed class Example
Copy the code

Sealed classes are abstract and cannot be instantiated, but can have abstract properties. Sealed classes are not allowed to have non-private constructors.

The following are common uses:

sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
data class Other(val number: Double) : Expr()
// When is equivalent to switch in Java, but has more powerful features such as type conversions, which we'll explain in the functions section
fun eval(expr: Expr): Double {
    return when(expr) {
        is Const -> expr.number
        is Sum -> eval(expr.e1) + eval(expr.e2)
        else -> Double.NaN
    }
}
Copy the code

The singleton Class Object Class

The use of singleton classes in Java is a common operation, and we will not show the implementation of a singleton in Java here (see this article). Implementation of singleton classes is supported natively in Kotlin:

object Singleton {
    fun doSomeThing(a) { / *... * /}}Copy the code

Objects declared this way are thread-safe and are instantiated only on the first access, with the JVM guaranteeing a singleton.

Calling its methods can be done in a manner similar to static access in Java:

Singleton.doSomeThing()
Copy the code

Singleton classes can also inherit from other parent classes. A singleton class can be nested in another class, but not in an inner class (i.e., not in inner class) :

class Test {
    object Singleton1		  / / can
    inner class Inner {
        object Singleton2 / / can't
    }
    class Inner2 {
        object Singleton3 / / can}}Copy the code

The object keyword also serves as a generation of anonymous inner classes. It is used as follows:

interface Teacher {
    fun speak(a)
}

fun giveAClass(t: Teacher) {
    t.speak()
}

fun main(a) {
    giveAClass(object : Teacher {
        override fun speak(a) {
            println("The temporary teacher began to speak.")}})// You can also use this method
    val point = object {
        var x: Int = 100
        var y: Int = 100
    }
    println(point.x)
}
Copy the code

This method of indirectly implementing transport functions through anonymous inner classes is relatively common in Java, but Kotlin has a method that works just fine, which we’ll cover in the functions section.

In addition, object can also be used as a keyword for an accompanying class.

Companion Class

Associated classes are declared as follows:

class MyClass {
    companion object Factory {
        val type = "A"
        fun create(a): MyClass {
            return MyClass()
        }
    }
}
Copy the code

The companion class allows us to use certain methods in a similar way to static method calls in Java, as in the example above:

val type = MyClass.type
val instance = MyClass.create()
/ / or
val type = MyClass.Factory.type
val instance = MyClass.Factory.create()
Copy the code

You can remove the Companion class name when declaring it, and Kotlin will automatically use Companion as the Companion class name. Note that ** there can only be one associated class in a class. ** Associated classes can also be inherited.

Inline Class Inline Class

Inline classes are currently being tested and may change later. The inline class is represented as follows:

inline class Wrapper(val value: Type)
Copy the code

In business, sometimes for some “things” business logic need to abstract, and creates the object represents the “stuff” at runtime may be heavier, increased the burden of system, the inner class is used to solve this problem, the inner class can be viewed as a kind of packing (Wrapper), it is to need packaging of abstraction, at compile time, Kotlin generates a wrapper for it, and at run time it is represented either by this wrapper or by the parameter types in its main constructor, so an inline class has one and only one main constructor property, which will probably replace the inline class at run time. Here’s a simple example:

inline class Password(val vaule: String)
Copy the code

This generates an inline class for the password string, which is easy to develop without the overhead of creating objects for the system. There is also an example of “representing”, such as Int in Kotlin, which can be represented by either the Java primitive Int or Integer.

Inline classes can inherit from, but not from, other classes. Functions and other attributes can be defined in inline classes, but these calls become static at run time.

Appoint the Delegation

Kotlin natively supports delegate mode, which means that we can delegate the work of one object to another object. In Kotlin, there are class delegate and property delegate, which are implemented by the keyword by. By delegate, we can delegate the initialization work of the parent class to the parameters accepted by the subclass:

interface Base {
    val value: Int
    fun printValue(a)
    fun print(a)
}
class BaseImpl : Base {
    override val value = 10
    override fun printValue(a) {
        println("BaseImpl value=${this.value}")}override fun print(a) {
        println("BaseImpl print")}}class Delegation(val base: Base) : Base by base {
    override val value = 20
    override fun print(a) {
        println("Delegation print")}}fun main(a) {
    val baseImpl = BaseImpl()
    val delegation = Delegation(baseImpl)
    delegation.printValue()
    delegation.print()
}
/ / output:
// BaseImpl value=10
// Delegation print
Copy the code

As you can see, after using a delegate object, the overridden method takes precedence when calling a method, but the delegate object can only access its own interface members (although the interface members are rewritten in Delegation, the delegate object can only access its own interface members when calling printValue(), Does not access interface members in Delegation).

Kotlin provides the lazy() function to implement attribute delegation by lazy({… }) to delegate attributes and implement lazy loading:

val lazyValue: String by lazy {
    println("Do something.")
    "Hello"
}
Copy the code

Lazy uses lambda expressions. If you’re not familiar with lambda expressions, it simply means that when the code runs here, it automatically executes the code in the block and takes the value of the last line as the block’s return value. Delegate through the lazy function, which initializes and remembers the lazyValue only the first time it is accessed, and returns the value directly on subsequent accesses.

Lazy () function is the default thread safe, if you need multiple threads at the same time can visit LazyThreadSafetyMode refs. The PUBLICATION; If you don’t need thread synchronization can be transmitted and LazyThreadSafetyMode. NONE.

Another common delegate method is to pass parameters using Map, which is often used to parse JSON as follows:

class User(valmap: Map<String, Any? >) {val name: String by map
    val age: Int by map
}
val user = User(mapOf(
    "name" to "Zhang"."age" to 20
))
println(user.name) / / zhang SAN
println(user.age)  / / 20
Copy the code