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
- Can run on the server side, Android, and anywhere Java runs
- 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
- Functional programming and object-oriented
- 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:
- 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" }
- Although the val ue reference itself is immutable, the object it points to may be mutable
val books = arrayListOf("Kotlin") books.add("Java")
- 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:
- 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)
- 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.
- Original type: boolean, char, byte, short, int, long, float, double
- 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.
- Kotlin's support for nullable types helps detect potential NullPointerException errors in the compiler
- Kotlin provides tools such as secure calls (?.), Elvis operators (?:), non-empty assertions (!!), let functions to concisely handle nullable types.
- The as? Operator provides a simple way to convert a value to a type and handle situations where it has different types
- Empty base data types (such as Int) correspond to boxed base data types in Java (such as java.lang.Integer)
- Types that represent basic numbers, such as Int, are usually compiled into Java basic data types
- The Any type is a supertype of all other types, similar to Java's Object, while the Unit class is analogous to void
- Functions that do not terminate normally use the Nothing type as a return type
- Kotlin uses standard Java collection classes and enhances them by distinguishing between read-only and variable collections
- Consider parameter controllability and variability when inheriting Java classes or implementing Java interfaces in Kotlin
- Kotlin's Array class, like a generic class, is compiled into a Java array
- 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
- The list itself is never null, but each value in the list can be null
- 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:
- The arrayOf function creates an array containing elements that are arguments specified for the function
- arrayOfNulls creates an array of a given size containing null elements.
- 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:
- 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
- The factory function (IntArray's intArrayOf) receives the values of variable-length parameters and creates an array to store them
- 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) }