Kotlin Core Syntax: Introduction to kotlin, Type System

Kotlin is a static language that runs on Java virtual machines, Android s, and browsers. It is 100% compatible with JAVA. If you are familiar with Java, you will find that Kotlin still uses the classic JAVA collection framework, in addition to its own standard libraries.

Introduction to kotlin

First try the Kotlin code.The Book class contains two properties: name and price.The default value of the price property is null.

// The default argument value for a nullable type (Float?) price is null
data class Book(val name: String, val price: Float? = null)

// Entry function
fun main(args: Array<String>) {
    val books = listOf(Book("Kotlin"), Book("Java", 13.9f))

    // lambda expression, maxBy finds the most expensive book in the books list
    // The Elvis cloud algorithm (?:) returns zero if the price attribute value is null
    val book = books.maxBy {
        it.price ?: 0.0f
    }

    println("the book is :$book")
}
// The toString method for automatically generating Book s
// the book is :Book(name=Java, price=13.9)

Main features of Kotlin

  1. Can run on the server side, Android, and anywhere Java runs
  2. Kotlin, like Java, is a static programming language, meaning that the type of all expressions is determined at compile time, and the compiler can verify that the object contains the method or field you want to access.Unlike dynamic languages such as Groovy, it cannot find problems such as misspelling names at compile time, resulting in runtime errors.

Kotlin has type inference capabilities, does not need to explicitly declare the type of each variable in the source code, automatically determines the type of variable x based on the context, val x = 1, automatically determines that the type of variable x is Int
Kotlin support for nullable types, detecting possible null pointer exceptions at compile time

  1. Functional programming and object-oriented
  2. Free and open source

http://github.com/jetbrains/k...

Anko, a library created by the Kotlin team http://github.com/kotlin/anko ) Kotlin-friendly adapters have been added to many standard Android API s.

Kotlin Tools

Kotlin and Java are compiled languages and must be compiled before code can be executed.

Kotlin Official Website
https://kotlinlang.org/docs/t...

Compile Kotlin code

The Kotlin source code is stored in a file with a suffix of.kt.The Kotlin compiler parses the source code and generates a.class file, then executes the generated.class file

$ kotlinc hello.kt -include-runtime -d hello.jar
$ java -jar hello.jar

Kotlin Foundation

Functions and Variables

function

// HelloWord.kt
// 1. Declare a function with the keyword fun
// 2. The type of the parameter is written after the parameter name, args: Array<String>
// 3. Functions can be defined at the outermost level of a file without having to be placed in a class
// 4. Arrays are classes and there is no special syntax for declaring array types in Kotlin, such as Array<String>
// 5. Use println instead of System.out.println.The Kotlin standard library provides many more syntactically concise wrappers for JAVA standard library functions
// 6. You can omit the semicolon at the end of each line of code

fun main(args: Array<String>) {
    println("Hello, Word!")
}

Declare a function with a return value followed by the return value type, separated by a colon (:)

// Declare format: fun function name (parameter list): return type {body}
fun max(a: Int, b: Int): Int {
    // In Kotlin, if is an expression, not a statement.An expression has a value and can be used as part of another expression
    return if (a > b) a else b
}

// If the body of a function is a single expression, the expression can be used as a complete body, and curly brackets and return statements can be omitted
fun max2(a: Int, b: Int): Int = if (a > b) a else b

// You can also omit the return value type.Note: Only return types of expression body functions can be omitted
fun max3(a: Int, b: Int) = if (a > b) a else b

variable

A variable identifies the address of an object, called an identifier.In Kotlin, all variable types are reference types.

In Kotlin, variables are divided into variable (var) and invariant (val)

  • var (variable) variable reference, the value of a variable can be changed, corresponding to a common (non-final) Java variable
  • val (value) is an immutable reference and cannot be re-assigned after initialization. It corresponds to the Java final variable

By default, all Kotlin variables are declared with the val ue keyword whenever possible.

Several points to note:

  1. Defining variables with value allows for only one initialization, but if the compiler can ensure that only one initialization statement is executed, different values can be used to initialize
    val message: String
    if (condition) {
        message = "Success"
    } else {
        message = "Failed"
    }
  1. Although the val ue reference itself is immutable, the object it points to may be mutable
   val books = arrayListOf("Kotlin")
   books.add("Java")
  1. The var keyword allows a variable to change its own value, but its type cannot be changed
    var age = 23
    // Error: Type mismatch
    age = "32"

In Kotlin, variable declarations start with keywords (val, var), followed by variable names.Finally, add the variable type (or not, the compiler will derive the type automatically)

val a: Int = 12

// Variable types can also be omitted, and the compiler will analyze the value of the initialization expression and use its type as the variable type
val a = 12

// If the variable is not initialized, you need to explicitly specify its type
val b: Int
b = 13

Java Type System


When a basic data type is created, it is stored in memory differently from a reference data type:

  1. When a basic data type is created, it is divided into a block of memory on the stack, and the values are stored directly on the stack (high performance)
  2. When a reference data type is created, a block of memory is allocated to its reference on the stack, and the specific information about the object is stored in the stack memory, then the reference on the stack points to the address of the object on the stack.

Every basic data type in Java introduces a wrapper class, such as Integer, which is the packaging type of int, and automatic boxing and unboxing mechanisms have been introduced since Java 5.

  1. Original type: boolean, char, byte, short, int, long, float, double
  2. Corresponding packaging types: Boolean, Char, Byte, Short, Integer, Long, Float, Double

Kotlin Type System

The original type is removed from Kotlin, only the packaging type. When the compiler compiles the code, it automatically optimizes the performance by unpacking the corresponding packaging type as the original type.

  1. Kotlin's support for nullable types helps detect potential NullPointerException errors in the compiler
  2. Kotlin provides tools such as secure calls (?.), Elvis operators (?:), non-empty assertions (!!), let functions to concisely handle nullable types.
  3. The as? Operator provides a simple way to convert a value to a type and handle situations where it has different types
  4. Empty base data types (such as Int) correspond to boxed base data types in Java (such as java.lang.Integer)
  5. Types that represent basic numbers, such as Int, are usually compiled into Java basic data types
  6. The Any type is a supertype of all other types, similar to Java's Object, while the Unit class is analogous to void
  7. Functions that do not terminate normally use the Nothing type as a return type
  8. Kotlin uses standard Java collection classes and enhances them by distinguishing between read-only and variable collections
  9. Consider parameter controllability and variability when inheriting Java classes or implementing Java interfaces in Kotlin
  10. Kotlin's Array class, like a generic class, is compiled into a Java array
  11. Arrays of basic data types are represented by special classes such as IntArray

1. Basic data types

Basic data types: Int, Boolean

Kotlin is a wrapper class that does not distinguish between basic data types and them.For example:Int

    val a: Int = 12
    val list: List<Int> = listOf(11, 12, 13)

Kotlin can also call methods on numeric values, and coerceIn is a standard library function that limits values to a specific range

    val a: Int = 110
    val newValue = a.coerceIn(0, 100)
    println("Limit values between 0 and 100, Value before limit: $a, Restricted value: $newValue")
   // Limit values between 0 and 100, before limit: 110, after limit: 100

Koltin's Int type is compiled into the Java base data type int. In addition to the generic class, the basic data type of the generic type parameter is compiled into the corresponding Java wrapper type, such as Int type compiled into java.lang.Integer

In Kotlin, the non-nullable basic data type corresponds to the original numeric type in Java, such as Int in Kotlin, which corresponds to int in Java;
An empty basic data type corresponds to a boxed type in Java, such as Int in Kotlin or Integer in Java.

Empty basic data types: Int?, Boolean?

Nulls can only be stored in variables of Java reference type, and null able basic data types in Kotlin cannot be represented by Java basic data types.

// Using the nullable basic data type (Int?) variable a, it is compiled into the java.lang.Integer type
fun isGreaterThan5(a: Int?): Boolean? {
    if (a == null) return null
    return a > 5
}

Digital Conversion

Kotlin does not automatically convert numbers from one type to another, for example, Int does not automatically convert to Long

 val a: Int = 12
 // Error in this line of code: type mismatch
 val b: Long = a 

// Conversion needed to display
val b: Long = a.toLong()

Koltin requires that the conversion must be displayed, especially when comparing boxed values.The equals method of comparing two boxed values not only checks the values they store, but also compares the boxed type.new Integer(12).equals(new Long(12)) returns false in Java.

 val a: Int = 12
 val list = listOf(12L, 13L, 14L)

 // This line of code compiler does not compile and requires the type of conversion to be displayed
 a in list

 // Displayed will convert Int to Long before it can be compared
 a.toLong() in list

The Kotlin standard library provides many extensions, such as string conversion to basic data types (toInt, toByte, toBoolean, etc.) and NumberFormatException thrown if conversion fails
println("12".toInt())

Root type: Any and Anny?

Java's supertype is Object, and all Kotlin's non-empty supertypes are Any.However, in Java, Object is only a supertype (root of reference type) of all reference types, while the basic data type is not.In Kotlin, Any is a supertype of all types, including the Int base data type.

// Values of basic data types are automatically boxed when assigned to variables of type Any
// Any is a non-null type and cannot hold null values. If you hold any possible values, including null, you must use any type?
val a: Any = 12

// Using the Any type in Kotlin compiles and converts to java.lang.Object

All Kotlin classes contain the following three methods: equals, hashCode, and toString.These three methods are defined in the Any class, but the Any class cannot use other java.lang.Object methods (such as wait, notify) and can be converted to java.lang.Object to invoke them.

Unit type: Kotlin's "void"

If a function does not return a value in Kotlin, you can use Unit as the function return type

fun f(): Unit { ... }

// Unit can be omitted
fun f(){ ... }

Kotlin's Unit differs from Java's void in that Unit can be used as a type parameter, but void cannot.When only one value is of type Unit, this value is also called Unit and is implicitly returned in the function.

interface Processor<T> {
    fun process(): T
}

class NoResultProcessor : Processor<Unit> {
    // Returns the Unit type, but can be omitted
    override fun process() {
        // No explicit return is required, and the compiler implicitly adds return Unit
    }
}

In Java, in order to solve the problem of using no value as a type parameter, the given scheme is not as good as Kotlin.One option is to use separate interface definitions to represent interfaces that need and don't need return values, such as Callable and Runnable, and the other is to use a special java.lang.Void type as a type parameter, but you still need to add a return null statement;

Nothing type: "This function never returns"

Nothing type has no value and is only meaningful when used as a function return value or as a type parameter for the return value of a generic function.

2. Empty

Nullable Type

Kotlin is display-supported for nullable types.For example: String?, Int?, you can store null references.Types without question marks indicate that this type cannot store null references, indicating that all default types are non-empty unless it is marked as empty.

 // Here's a piece of java code
 // If a function is called with null passed in, a NullPointerException is thrown
 int strLen(String s) {
    return s.length();
}


// Here is a Kotlin code
// If a function is called with null passed in, the kotlin compiler does not allow it, guaranteeing that the strLen function will never throw a NullPointerException
// Compile-time flags as errors: Null can not be a value of a non-null type String
fun strLen(s: String): Int = s.length

In the example above, if null is allowed when calling the strLen function, a question mark (?) is needed after the parameter type.

// ? Variables that can be added after any type to indicate this type can store null references
fun strLenSafe(s: String?): Int = ...

// The following Kotlin code, the s.length compiler, is not allowed
// ERROR: only safe (?) or non null asserted (!!.) calls are allowed 
// on a nullable receiver of type kotlin.String?
// You can use if checks to handle controllability, but the code becomes redundant.But Kotlin provides a simpler way to handle nullable values
fun strLenSafe(s: String?): Int = s.length

// The following Kotlin code, which is also not allowed by the compiler, cannot be assigned to variables of non-empty type
// ERROR: Type mismatch:Required String , Found String?
val a: String? = null
val b: String = a

Security call operator:'?'.

Security Call Operator:?. Allows null checks and method calls to be combined into one operation.

string?.length()
//Equivalent to
if (string != null) string.length() else null

Security Call Operator:?. The result type of the call is also nullable. In the following example, s?.toUpperCase() is the result type String?

fun printAllCaps(s: String?) {
    // allCaps may be null
    val allCaps: String? = s?.toUpperCase()
    println(allCaps)
}

 >>> printAllCaps(null)
 null

Multiple security call operators can link calls

class Address(val name: String)

class Company(val name: String, val address: Address?)

class Person(val name: String, val company: Company?)

fun Person.printAddress(): String? {
    // Multiple security call operator link calls
    val addressName =  this.company?.address?.name
    // Elvis operator:?: addressName?:"Unknown"
    return if (addressName != null) addressName else "Unknown"
}

>>>  val person = Person("kerwin", null)
>>>  println(person.printAddress())
Unknown

Elvis operator: "?:"

Kotlin uses the Elvis operator (or null merge operator) to provide default values instead of null.

Elvis operator?: and security call operator?.

// When s==null, s?.length returns null
// s?.length == null returns 0 
fun strLenSafe(s: String?): Int? = s?.length ?: 0

>>> println(strLenSafe("abc"))
3

>>> println(strLenSafe(null))
0

Security conversion: "as?"

The as? Operator attempts to convert a value to the specified type and returns null if the value is not of the appropriate type

class Person(val firstName: String, val lastName: String) {

    override fun equals(other: Any?): Boolean {
        // If other is not of Person type, other as? Person returns null, which returns false directly
        val otherPerson = other as? Person ?: return false

        return otherPerson.firstName == firstName && otherPerson.lastName == lastName
    }
}

>>>   val p1 = Person("Kerwin", "Tom")
>>>   val p2 = Person("Kerwin", "Tom")
>>>   println(p1 == p2)
true

Non-empty assertion: "!!"

A non-empty assertion uses a double exclamation mark (!!) to indicate that any value can be converted to a non-empty type, and an exception will be thrown if a non-empty assertion is made on a null value.

fun ignoreNulls(s: String?) {
    // Throw KotlinNullPointerException if s == null
    val sNotNull = s!!
    println(sNotNull.length)
}

>>> ignoreNulls(null)
Exception in thread "main" kotlin.KotlinNullPointerException

"let" function

Used in conjunction with security call operators, allows evaluation of expressions, checks whether the evaluation result is null, and saves the result as a variable.

The let function is called only when the value is not empty

fun sendMessage(message: String) {
    println(message)
}

// The lambda expression is executed when message!= null
>>>  val message: String? = null
>>>  message?.let { msg -> sendMessage(msg) }
>>> // It default variable name, can abbreviate message?.let {sendMessage(it)}

Delayed Initialization Properties

Many frameworks use specialized methods to initialize objects after they are created.For example: Activity initialization occurs in the onCreate method, and JUnit requires that the initialization logic be placed in the method annotated with @Before.

class MyService{
    fun performAction(): String = "test"
}

class MyTest {
    // Declare an empty type property and initialize it to null
    private var myService: MyService? = null

    @Before fun setup() {
        // Provide a real initializer in the setup method
        myService = MyService()
    }

    @Test fun testAction() {
        // Must be aware of nullability: either use!!, or use?.
        Assert.assertEquals("test", myService!!.performAction())
    }
}

In the kotlin code above, nullability is required for every access to the property myService. To solve this problem, Kotlin can declare the property myService as delayed initialization, using the lateinit modifier

class MyService{
    fun performAction(): String = "test"
}

class MyTest {
    // Use lateinit to declare a property of a non-empty type that does not require an initializer
    // Delayed initialization properties are var
    // If the property is accessed before it is initialized, throw an exception: lateinit property myService has not been initialized
    private lateinit var myService: MyService

    @Before fun setup() {
        myService = MyService()
    }

    @Test fun testAction() {
        // Direct access properties without null check
        Assert.assertEquals("test", myService.performAction())
    }
}

Extensions of nullable types

Defining an extension function for nullable types is a more powerful way to handle null values.The two extension functions isEmpty and isBlank for String defined in the Kotlin standard library.Functions isEmptyOrNull and isNullOrBlank can be called by recipients of type String?

fun verifyUserInput(input: String?) {
    // Values of nullable types. Extensions of nullable types
    if (input.isNullOrBlank()){  // No security call required
        println("input is null or blank.")
    }
}

>>> verifyUserInput(null)
input is null or blank.

// Function isNullOrBlank implementation, an extension of empty strings
// return this == null || this.isBlank()

Nullability of type parameters

Type parameters for all generic classes and functions in Kotlin are empty by default.Any type, including nullable types, can replace type parameters.Declarations using type parameters as types are allowed to be null, although type parameter T does not end with a question mark

fun <T> printHashCode(t: T) {
    // Because t may be null, a security call must be used, and the argument t allows nulls even though there is no end of the question mark
    println(t?.hashCode())
}

// T is deduced to Any?
>>> printHashCode(null)
null

To make a type parameter non-empty, you must specify a non-empty upper bound for it

// T is not empty now
fun <T: Any> printHashCode(t: T) {
    println(t.hashCode())
}

// The compiler does not allow null to be passed because the expected value is not null
>>> printHashCode(null)
Null can not be a value of a non-null type TypeVariable(T)

Nullability and Java

Use annotations to express nullability in Java, such as @Nullable String treated as String by Kotlin, and @NotNull String treated as String by Kotlin.

3. Sets and Arrays

Collection libraries in Kotlin are built on Java and enhanced by the added features of extension functions.

Nullability and Collection

Kotlin supports nullability of type parameters.But be careful what is nullable: the elements of the collection or the collection itself

  1. The list itself is never null, but each value in the list can be null
  2. Variables of type may contain empty references rather than list instances, but elements in lists are guaranteed to be non-empty
// List<Int?>Can hold a list of Int? Type values, that is, Int or null
fun readNumbers(reader: BufferedReader): List<Int?> {
    // Create a list containing nullable Int values
    val result = ArrayList<Int?>()
    for (line in reader.lineSequence()) {
        println("line: $line")

        try {
            // Add a non-null integer to the list
            val number = line.toInt()
            result.add(number)
        } catch (e: NumberFormatException) {
            // Parsing failed, add null value to list
            result.add(null)
        }
    }

    return result
}

When using a set of nullable values, a null check is required

// List<Int?>? Declares that a variable holds an empty list and contains empty numbers
// List<Int?>Declares a list of variables that are not null and contain empty numbers
fun addValidNumbers(numbers: List<Int?>) {
    var sumOfValidNumbers = 0
    var invalidNumbers = 0
    for (number in numbers) {
        // Check if null
        if (number != null) {
            sumOfValidNumbers += number
        } else {
            invalidNumbers++
        }
    }

    println("sum of valid numbers:$sumOfValidNumbers")
    println("Invalid numbers:$invalidNumbers")
}


// This can be accomplished by using the standard library function filterNotNull() provided by Kotlin, traversing a set containing nullable values and filtering out nulls
// However, filterNotNull() returns a collection type that does not contain any null elements, so it returns a collection type such as List<Int>

Read-only and variable collections

Kotlin separates the interface to access the collection data from the interface to modify it.General rule: Read-only interfaces should be used anywhere in the code, and variations of variable interfaces should be used wherever the code needs to modify the collection.However, variables of read-only collection types cannot be assigned to variable collection variables.

From the kotlin.collections.Collection interface, you can see that you can iterate through the elements in a collection, get the size of the collection, and determine if an element is included in the collection.This interface has no methods to add or remove elements.

public interface Collection<out E> : Iterable<E> {
 
    public val size: Int

    public fun isEmpty(): Boolean

    public operator fun contains(element: @UnsafeVariance E): Boolean

    override fun iterator(): Iterator<E>

    public fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean
}

The kotlin.collections.MutableCollection interface can modify data in a collection.

public interface MutableCollection<E> : Collection<E>, MutableIterable<E> {
   
    override fun iterator(): MutableIterator<E>

    public fun add(element: E): Boolean

    public fun remove(element: E): Boolean

    public fun addAll(elements: Collection<E>): Boolean

    public fun removeAll(elements: Collection<E>): Boolean

    public fun retainAll(elements: Collection<E>): Boolean

    public fun clear(): Unit
}

Note: Read-only collections are not always thread safe.

Set Creation Function

Collection Type read-only variable
List listOf mutableListOf,arrayListOf
Set setOf mutableSetOf, hashSetOf,linkedSetOf,sortedSetOf
Map mapOf mutableMapOf,hashMapOf,linkedMapOf,sortedMapOf

Array of objects and basic data types

By default, collections should take precedence over arrays.

An array in Kotlin is a class with a type parameter whose element type is specified as the corresponding type parameter

fun printArray(args: Array<String>) {
    // Iterate over the scope of the subscript using the extended attribute args.indices
    for (i in  args.indices) {
        // Access elements using array[index] through Subscripts
        println("Argument $i is ${args[i]}")
    }
}

Create an array in Kotlin:

  1. The arrayOf function creates an array containing elements that are arguments specified for the function
  2. arrayOfNulls creates an array of a given size containing null elements.
  3. The Array constructor accepts the size of the array and a lambda expression, calling the lambda expression to create each array element.This is the way to initialize an array using a non-empty element type without explicitly passing each element.
   // Using the Array constructor to create an array, you can omit the type of array elements
   val letters = Array<String>(26) {
        // A lambda expression receives the subscript of an array element and returns the value placed at the subscript of the array
        i -> ('a' + i).toString()
    }
    println(letters.joinToString(""))

//abcdefghijklmnopqrstuvwxyz

One of Kotlin's most common scenarios for creating arrays is to call a Java method with an array parameter or a Kotlin function with a vararg parameter.

  // Pass collection to vararg method
  val strings = listOf("a", "b", "c")
  // fun String.format(vararg args: Any?): String
  // Passing an array with the expansion operator (*) when the vararg parameter is expected
  // Convert a collection to an array using the toTypedArray method
  println("%s/%s/%s".format(*strings.toTypedArray()))

//  a/b/c

Kotlin provides several separate classes representing arrays of basic data types, such as an array of Int-type values called IntArray, ByteArray, CharArray, BooleanArray, and so on.They correspond to arrays of Java basic data types, such as int[], byte[], char[].The median values of these arrays are stored unboxed and are most efficient.

Create an array of basic data types in Kotlin:

  1. The constructor for this type receives the size parameter and returns an array initialized with the default value (usually 0) for the corresponding base data type
  2. The factory function (IntArray's intArrayOf) receives the values of variable-length parameters and creates an array to store them
  3. Another construction method that receives a size and a lambda to initialize each element
// Create an integer array that stores 5 0
1, val arr1 = IntArray(5)
2, val arr2 = intArrayOf(0, 0, 0, 0, 0)
3, val arr3 = IntArray(5) {
        i -> 0
    }

public fun intArrayOf(vararg elements: Int): IntArray


public class IntArray(size: Int) {
    public inline constructor(size: Int, init: (Int) -> Int)
}

Keywords: Android Java Lambda Attribute

Added by NeilB on Mon, 25 Nov 2019 05:33:10 +0200