Chapter 2 Introduction to Kotlin

  1. The working principle of

    Programming languages fall into two broad categories: compiled and interpreted. Java is an interpreted language. Java code is compiled to generate class files that are recognized only by the Java virtual machine. The Java virtual machine acts as an interpreter, interpreting the mutated class file as binary data that the computer can recognize before executing the program.

    Kotlin’s compiler compiles Kotlin code into class files. The Java virtual machine doesn’t care if a class file is compiled from Java or Kotlin, it recognizes a class file as long as it meets the specification. That’s how Kotlin works.

  2. Variables and functions

    1. variable

      Only two keywords are allowed to be declared before a variable: val and var

      Val (short for value) declares an immutable variable, always using val in preference to declaring the variable

      Var (variable short) declares a mutable variable

      val a: Int = 10
      Copy the code

      Kotlin completely ditches the basic data types in Java and uses all object data types.

      Shift + CTRL + P displays variable types

    2. Basic types of

      Kotlin unwrapped type (Byte, Integer…)

      Byte, int, Float&Long, Char, String

      // val c = 12345678910l // compile error.
      // L should be capitalized
      val c = 12345678910L // ok
      Copy the code
      val e: Int = 10
      //val f: Long = e // implicitness not allowed
      // Explicit conversion, implicit conversion does not compile
      val f: Long = e.toLong() // implicitness not allowed
      Copy the code
      val k = "Today is a sunny day."
      val m = String("Today is a sunny day.".toCharArray())
      
      // Compare addresses
      println(k === m) // compare references.
      // If the value is equal, Java compares the address. Java compares the content using equals
      println(k == m) // compare values.
      Copy the code
    3. Unsigned type

      UByte, integer (UShort&UInt&ULong)

      val g: UInt = 10u
      val h: ULong = 100000000000000000u
      Copy the code
    4. String template

      val slogan = "To be No.1"
      println("Value of String 'j' is: $slogan") // no need brackets
      println("Length of String 'j' is: ${slogan.length}") // need brackets
      Copy the code
      val sample = "" "
                   
                 Hello World   
                

      Hello World

      This is a demo page.

      """
      .trimIndent() println(sample) Copy the code
  3. An array of

    IntArray: IntArray, Int boxing Array: Array

    Integer is a wrapper type for int, which is more wasteful than int. As a good coder, you’ll want to use int instead of Integer if the scope allows.

    So let’s look at two approaches

    fun myList(vararg inner: Int) {
        println(inner.size)
    }
    
    fun myList2(vararg inner2: Int?). {
        println(inner2.size)
    }
    Copy the code

    The only difference between these two methods is do you have an Int after it? Number. Let’s look at the important parts of the decompiled code first

    Tools->Kotlin->Show Kotlin Bytecode->Decompile

    public static final void myList(@NotNull int... inner) {
       Intrinsics.checkNotNullParameter(inner."inner");
       int var1 = inner.length;
       boolean var2 = false;
       System.out.println(var1);
    }
    
    public static final void myList2(@NotNull Integer... inner2) {
       Intrinsics.checkNotNullParameter(inner2, "inner2");
       int var1 = inner2.length;
       boolean var2 = false;
       System.out.println(var1);
    }
    Copy the code

    Look carefully at the signature of the parameter

    • MyList is int… inner
    • MyList2 is an Integer… inner2

    You can wonder why the Kotlin compiler compiles this way. Right? In fact, the root cause is? , can be empty. When the Kotlin compiler decides that a parameter cannot be null, it is perfectly possible to use unwrapped types to accept parameters, making more efficient use of resources.

    val intArrayOf = intArrayOf(1.3.4)
    myList(*intArrayOf)
    myList(1.2.3)
    
    // Notice the null here
    val array = arrayOf(1.3.4.null)
    myList2(*array)
    myList2(1.2.3)
    Copy the code

    The above code needs to be noted:

    • statementIntAn array of type,KotlinTwo methods are provided, one isintArrayOf(), one isarrayOf();
    • intArrayOfStress isint, similar toJavaIn theint[], so the parameter cannot benull
    • arrayOfsimilarJavaIn theInteger[], so you can assign to itnull
    • Arrays in Kotlin cannot be assigned directly to morphable parametersvararg, you need to pass the keyword*
    • Reflect on that when we createarrayIf you don’t need tonullAre you usingintArrayBetter?
    val array = arrayOf(1.3.4.null)
    // Prints the contents of the array
    println(array.contentToString())
    // Get the length
    println(array.size)
    // reassign
    array[1] = 4
    / / value
    println(array[0])
    / / traverse
    for (i in array) {      
    }
    // Check if the value is in the array
    if (1 in array) {
      println("1 exists in variable 'array'")}Copy the code
  4. interval

    // [0,10] discrete values
    val intRange: IntRange = 1.10.
    / / [0, 10)
    val intRange1: IntRange = 1 until 10
    / / [10, 9,..., 1]
    val intProgression = 10 downTo 1
    
    / / step length
    val intProgression1 = 1.10. step 2
    for (i in intProgression1) {
      // 1 3 5 7 9
      print("$i  ")}// 1, 3, 5, 7, 9
    println(intProgression1.joinToString())
    Copy the code
    // [1,2] continuous values
    val closedFloatingPointRange: ClosedFloatingPointRange<Float> = 1f..2f
    Copy the code
    val intArrayOf = intArrayOf(1.2.3.4.5)
    for (i in 0 until intArrayOf.size) {
        print("${intArrayOf[i]} ")}// intArrayOf.indices [0,intArrayOf.size)
    for (i in intArrayOf.indices) {
        print("${intArrayOf[i]} ")}Copy the code
  5. A collection of

    • The immutable set is added

      Immutable List

      , MutableList MutableList

      Map…

      Set…

      // Elements cannot be added or removed
      val listOf = listOf(1.2.3)
      // Elements can be added or removed
      val mutableListOf = mutableListOf(1.2.3)
      
      val mutableMapOf = mutableMapOf("1" to "first"."2" to "Second")
      for ((x, y) in mutableMapOf) {
              
      }
      val pair = "Hello" to "Kotlin"
      val pair1 = Pair("Hello"."Kotlin")
      
      val first = pair.first
      val second = pair.second
      val (x, y) = pair
      Copy the code
      / / Java implementation
      List<Integer> integers = Arrays.asList(1.2.3);
      // Run an error
      integers.add(1);
      // The content is mutable
      integers.set(1.3);
      
      // The content is immutable
      Collections.unmodifiableList(integers)
      
      ArrayList arrayList = new ArrayList();
      arrayList.add(1);
      Copy the code
    • Provides a wealth of easy-to-use methods

    • Operator-level support to simplify access to the collection framework

  6. function

  7. The function definitions

    Fun (Short for function) Defines the key of a function. Fun is followed by the function name. This is followed by a pair of parentheses that contain the parameters. If there is no return value, you can write Unit. Unit is equivalent to the return value of the Java void function.

    fun functionName(arg1: Int, arg2: Int): Int {
        return 1
    }
    Copy the code

    When a function has only one line of code, Kotlin allows you to write a single line of code at the end of the function definition, with an equal sign in the middle.

    fun largeNumber(arg1: Int, arg2: Int) = max(arg1, arg2)
    Copy the code
  8. Function vs method

    • Methods can be thought of as a special type of function
    • Formally, functions that have receivers (Java objects, handles) are methods
  9. Type of function

    fun foo(a) {}  // The type is () -> Unit
    val a: () -> Unit = {}
    
    // Foo is the receiver for the bar method. In Java, this is passed in as the first argument to the function without displaying the call
    // Function references are similar to function Pointers in C, used for function passing
    // val kFunction: (Foo, String, Long) -> Long = Foo::bar
    // val kFunction2: Foo.(String, Long) -> Long = Foo::bar
    // val kFunction3: Function3
            
              = Foo::bar
            ,>
    class Foo{
        fun bar(p1:String, p2:Long) : Long {
            return p1.length + p2
        }
    }
    
    val x: (Foo, String, Long) -> Any = Foo::bar
    yy(x)
    fun yy(p: (Foo.String.Long) - >Any) {
        p(Foo(), "Hello".3L)
        p.invoke(Foo(), "Hello".3L)}Copy the code
  10. Logical control of a program

    Program execution statements are divided into sequential statements, conditional statements and circular statements.

    1. Conditional statements

      1. If conditional statement

        If conditional statements are no different from Java

        The if statement in Kotlin has an additional feature over the Java version in that it can have a return value

        fun largeNumber(num1: Int, num2: Int): Int {
            val largeInt = if (num1 > num2) {
                num1
            } else {
                num2
            }
            return largeInt
        }
        Copy the code
      2. When conditional statement

        When is similar to switch in Java

        Switch bug: Only char, byte, short, int, Character, byte, short, Integer, String, or an enum variables can be passed as conditions. In addition, each case must break; otherwise, execute the following cases after executing the current case.

        Basic examples:

        fun getScore(name: String) = when (name) {
            "Tom" -> 85
            "Jim" -> 77
            else -> 0
        }
        Copy the code

        Example of type matching:

        fun checkNumber(num: Number) = when (num) {
            is Int -> "Int"
            is Double -> "Double"
            else -> "not support type"
        }
        Copy the code

        Is is a type matching keyword, equivalent to the Instanceof keyword in Java.

        For the example of when without arguments, Kotlin uses the **== keyword to determine strings or objects, rather than the Equals () ** method used in Java

        fun getScoreNoParam(name: String) = when {
            name.startsWith("lisa") - >99
            name == "Tom" -> 85
            name == "Jim" -> 77
            else -> 0
        }
        Copy the code
    2. Looping statements

      1. For loop

        The For-i loop in Java has been abandoned and the for-each loop in Java has been enhanced to become a for-in loop

        Kotlin’s interval is written as follows:

        val range = 0.10. / / [0, 10]
        val range2 = 0 until 10 / / [0, 10)
        Copy the code
        for (i in 0.10.) {
            print("$i  ")
            // Output 0 1 2 3 4 5 6 7 8 9 10
        }
        Copy the code

        Skipping odd elements using the step keyword is equivalent to the Java I = I + 2 operation

        for (i in 0 until 10 step 2) {
            print("$i  ")
            // Output 0, 2, 4, 6, 8
        }
        Copy the code

        DownTo defines the descending range

        for (i in 10 downTo 0) {
            print("$i  ")
            // Output 10 9 8 7 6 5 4 3 2 10
        }
        Copy the code
      2. The while loop is no different from the Java while loop in both syntax and usage techniques

  11. Object-oriented programming

    1. class

      File is typically used to write Kotlin top-level functions and extension functions

      Class is the Class used to write Kotlin

      Classes are decorated with public by default

      Example:

      class Person {
          var name = "Tony"
          var age = 10
      
          fun introduceSelf(a) {
              println("My name is $name. I am $age years old.")}}Copy the code

      Class instantiation:

      val person = Person()
      person.introduceSelf()
      Copy the code
    2. Inheritance and constructors

      1. Define the Student class to inherit from Person:

        1. Make Person inheritable by adding the open keyword to Person. Any non-abstract class in Kotlin cannot be inherited by default, which is equivalent to declaring the final keyword for a class in Java.

          open class Person {... }Copy the code
        2. Make Student inherit from Person

          class Student : Person() {
            init{... }... }Copy the code

          Why parentheses after Person: Subclass constructors must call superclass constructors.

          The constructors in Kotlin are: primary constructor and secondary constructor. The main constructor does not have a function body; Kotlin provides an init structure. Which constructor of the parent class is called by the primary constructor of a subclass is specified by parentheses during inheritance.

      2. Transform the Person

        Person transformation is as follows:

        open class Person(val name:String, val age:Int) {
            fun introduceSelf(a) {
                println("My name is $name. I am $age years old.")}}Copy the code

        Student adjusts accordingly:

        class Student(val number: Int.val grade : Int, name:String, age:Int) : Person(name, age) {
            init{}}Copy the code

        The name and age in the Student constructor cannot be declared as var or val, because declaring val or val would be a field of the class.

      3. subconstructor

        Classes can have only one primary constructor, but they can have more than one secondary constructor. All secondary constructors must call the primary constructor.

        The following is an example:

        class Student(val number: Int.val grade: Int, name: String, age: Int) : Person(name, age) {
            constructor(name: String, age: Int) : this(0.0, name, age) {
            }
        
            constructor() : this("Tom".0) {}}Copy the code

        The subconstructor is defined by constructor. The instantiation code is as follows:

        val student1 = Student()
        val student2 = Student("Jack".10)
        val student3 = Student(1527.2."Jerry".21)
        Copy the code

        Class has only secondary constructors, no primary constructors:

        class Student : Person {
        
            constructor(number: Int, grade: Int, name: String, age: Int) : super(name, age) {
        
            }
        
            constructor(name: String, age: Int) : this(0.0, name, age) {
        
            }
        
            constructor() : this("Tom".0) {}}Copy the code

        Arguments cannot be declared by var or val in a subconstructor, because the arguments declared by var or val become fields of the class. The secondary constructor does not have to be called, so it is not initialized because it is not called, so it is not allowed to use var or val declarations.

    3. An abstract class

      abstract class AbsClass {
          abstract fun absMethod(a)
          // Add open before overwriting
          open fun overridable(a){}
          fun nonOverridable(a){}}Copy the code

      Method before the open keyword, can be copied

      Add the final keyword if the inherited parent class has a method that can be overridden, but does not want the method to be overridden by a subclass

      open class SimpleClass(var x: Int.val y: String)
          : AbsClass() {
      
          override fun absMethod(a) {}
      
          // Word classes that inherit SimpleClass cannot override this method
          final override fun overridable(a){}}Copy the code
    4. interface

      The interfaces in Kotlin are almost identical to those in Java.

      1. Interface use:

        1. Creating the Study Interface

          interface Study {
              fun readBooks(a)
              fun doHomework(a)
          }
          Copy the code
        2. Implementing an interface

          class Student(name: String, age: Int) : Person(name, age), Study {
              override fun readBooks(a){}override fun doHomework(a){}}Copy the code

          Those of you familiar with Java know that the extends keyword is inherited, implements is implemented, and Kotlin uses colons separated by commas.

        3. Example:

          fun main(a) {
              val student = Student("Jack".10)
              doStudy(student)
          }
          
          fun doStudy(study: Study) {
              study.doHomework()
              study.readBooks()
          }
          Copy the code

          Because Student implements the Study interface, the Student class can be passed to the doStudy function and then call the methods of the Study interface. This is called interface oriented programming, or polymorphism.

      2. The default implementation

        interface Study {
            fun readBooks(a)
            fun doHomework(a) {
                println("doHomework default implementation")}}Copy the code

        Functions implemented by default are optional to implement or not implement. No implementation will use the default implementation logic.

      3. Visibility modifier

        The modifier Java Kotlin
        public All classes visible All classes visible (default)
        private Current class visible Current class visible
        protected Visible to current class, subclass, and class under the same package The current class and subclass are visible
        default Classes visible under the same package (default) There is no
        internal There is no Classes visible in the same module
      4. property(Kotlin) vs field(Java)

        Kotlin property = Java (field + get + set)

        If val is qualified, only get

        Get + set if var modifier

        class Person(age: Int, name: String) {
            var age: Int = age //property
                get() {
                    return field
                }
                set(value) {
                    println("setAge: $value")
                    field = value
                }
            var name: String = name
                get() {
                    return field // backing field
                }
                set(value) {
        
                }
        }
        Copy the code

        Attribute references

        // No receiver is bound
        val ageRef = Person::age
        val person = Person(18."Bennyhuo")
        / / bind the receiver
        val nameRef = person::name
        ageRef.set(person, 20)
        nameRef.set("Andyhuo")
        Copy the code
    5. Data classes and singleton classes

      1. Data classes

        Data classes typically need to override equals(), hashCode(), and toString() methods

        Equals () determines whether data is equal

        HashCode () HashMap, HashSet and other hash-related classes work properly

        ToString () clear log output

        Example:

        data class Cellphone(val brand: String, val price: Double)
        Copy the code

        Declaring data before a class indicates that the class is a data class. Kotlin automatically implements equals(), hashCode(), toString() and other fixed methods that make no real logical sense.

      2. Singleton class

        There is only one instance globally

        object Singleton {
            fun singletonTest(a) {
                println("SingletonTest is called")}}Copy the code

        use

        Singleton.singletonTest()
        Copy the code
  12. Lambda programming

    1. Collection creation and traversal

      1. Create a List

        listOf<String>("Hello"."world"."this"."is"."kotlin")
        Copy the code
      2. Traverse the List

        for (s in listOf) {
            println(s)
        }
        Copy the code
      3. The listOf() function creates an immutable collection, while the mutableListOf() function is a mutable collection

        val mutableList = mutableListOf<String>("Hello"."world"."this"."is"."kotlin")
        Copy the code
      4. Create a Map

        var fruitAndPrice :HashMap<String, Int> = HashMap()
        fruitAndPrice["Apple"] = 3
        
        val fruitAndIndex = mapOf("Apple" to 1."Banana" to 2."Orange" to 3."pear" to 4."Grape" to 5)
        Copy the code
      5. Traverse Map

        for ((fruit, number) in fruitAndIndex) {
            println("fruit is $fruit, index is $number")}Copy the code
    2. The functional API for collections

      1. Take the word with the longest length

        val wordList = listOf<String>("Hello"."world"."this"."is"."kotlin")
        // When there are no elements in the set, the result is empty
        val maxByOrNull = wordList.maxByOrNull { it.length }
        // Empty string is used when the result is empty
        valresult: String = maxByOrNull ? :""
        println(result)
        Copy the code
      2. Lambda is a piece of code that can be passed as an argument, Lambda syntax structure

        {parameter name1: Parameter type, parameter name2: Parameter type -> function body}Copy the code
      3. Simplified derivation

        The maxByOrNull above is just a simple function whose internal implementation iterates through the collection and calls a lambda expression (the collection element is passed into the expression) to get the length of the elements and compare, eventually returning the longest collection element.

        1. Lambda expressions as variables

          val wordList = listOf<String>("Hello"."world"."this"."is"."kotlin")
          val function = { fruit: String -> fruit.length }
          val maxByOrNull = wordList.maxByOrNull(function)
          Copy the code
        2. Pass it directly as a parameter

          val maxByOrNull = wordList.maxByOrNull({ fruit: String -> fruit.length })
          Copy the code
        3. When lambda is the last argument to a function, you can move the lambda expression out of parentheses

          val maxByOrNull = wordList.maxByOrNull() { fruit: String -> fruit.length }
          Copy the code
        4. If lambda is the only argument to a function, you can omit the parentheses

          val maxByOrNull = wordList.maxByOrNull { fruit: String -> fruit.length }
          Copy the code
        5. Kotlin has an excellent type derivation mechanism, and the argument lists in Lambda expressions don’t actually have to declare parameter types in most cases

          val maxByOrNull = wordList.maxByOrNull { fruit -> fruit.length }
          Copy the code
        6. When there is only one argument in the argument list of a Lambda expression, there is no need to declare the argument name, but the IT keyword can be used instead

          val maxByOrNull = wordList.maxByOrNull { it.length }
          Copy the code
      4. Java functional API usage

        Limitation: The functional API can be used when a Java method is called in Kotlin code and the method receives a Single Abstract Java method interface argument. A Java single abstract method interface is one in which there is only one method to implement. If there are multiple methods to implement in an interface, functional apis cannot be used.

        Take Runnable for example:

        1. Java implementation:

          new Thread(new Runnable() {
              @Override
              public void run(a) {
                  TODO("Not yet implemented")}});Copy the code
        2. Kotlin implementation:

          Thread(object : Runnable {
              override fun run(a) {
                  TODO("Not yet implemented")}})Copy the code

          Kotlin discarded the new keyword in favor of the object keyword

          Again, this is simplified because Runnable has only one method to implement, without explicitly overwriting the run() method

          Thread(Runnable {
              TODO("Not yet implemented")})Copy the code

          We can also omit the interface name if there is only one Java single abstract method interface parameter in the argument list of a Java method

          Thread({
              TODO("Not yet implemented")})Copy the code

          If the Lambda expression is still the only argument to the method, you can also omit the parentheses of the method

          Thread {
              TODO("Not yet implemented")}Copy the code
  13. Null pointer check

  14. Nullable types

    fun doStudy(study: Study) {
        study.doHomework()
        study.readBooks()
    }
    Copy the code

    There is no null pointer risk in this code, because Kotlin defaults to non-null parameters and variables, so the Study parameter passed here must not be null either.

    Kotlin has moved null-pointer exception checks up to compile time, so if our program is at risk of null-pointer exceptions, it will report an error at compile time.

    Allow null example:

    fun doStudy(study: Study?). {
        if(study ! =null) {
            study.doHomework()
            study.readBooks()
        }
    }
    Copy the code
  15. Void detection AIDS

    1. With the aid of? The. Operator removes the if statement

      fun doStudy(study: Study?).{ study? .doHomework() study? .readBooks() }Copy the code
    2. ? The: operator, which receives an expression on both sides and returns the result of the left expression if the left expression is not empty, or the right expression otherwise

      General writing:

      val a:String = "a"
      val c = if (a == null) a else ""
      Copy the code

      ? : operator:

      valc = a ? :""
      Copy the code

      The example gets the length of the string

      fun getWordLength(word : String) : Int {
          if (word == null) {
              return 0
          } else {
              return word.length
          }
      }
      Copy the code

      Simplified:

      fun getWordLength(word : String?). : Int {
          returnword? .length ? :0
      }
      Copy the code
    3. *Non-empty assertion tool, not recommendedThis is a risky way to write it

      fun getWordLength(word : String?). : Int {
          returnword!! .length }Copy the code
    4. Auxiliary tool — let

      fun doStudy(study: Study?).{ study? .let { stu -> stu.readBooks() stu.doHomework() } }Copy the code

      The let function can handle global nulling, whereas the if statement cannot.

      We changed the argument in the doStudy() function to a global variable, which still works fine using the let function, but will prompt an error using the if statement.

      The reason for this error is that the value of the global variable can be changed by another thread at any time, and even if nulled, there is no guarantee that the study variable in the if statement is free from null pointer risk.

      var student: Student? = Student("Lucy".19)
      
      fun study(a) {
          if(student ! =null) {
              student.doHomework()
              student.readBooks()
          }
      }
      Copy the code
  16. Kotlin small magic

  17. String embedded expression

    class Student(name: String, age: Int) : Person(name, age), Study {
        override fun readBooks(a) {
            println("My name is $name, I'm ${age} years old. I'm reading!")}}Copy the code
  18. Function default arguments

    fun sing(songName:String = "Pretty Girl", artist:String){}Copy the code

    Call:

    sing("hello"."jams")
    // Assign via key-value pairs
    sing(artist = "lucy")
    Copy the code

    Add default values to main constructors to avoid multiple constructors

    class Student(val number: Int = 0, val grade: Int = 0, name: String="Tom", age: Int=10) : Person(name, age), Study {
    
    }
    Copy the code