Public number: byte array, hope to help you 🤣🤣
Extension functions and extension properties
15.1. Extension functions
Extension functions are used to add a new behavior to a class, which is a way to extend a class that lacks useful functions. The purpose of extension functions is similar to the static tool methods implemented in Java. One advantage of using extension functions in Kotlin is that we don’t need to pass in the entire object as an argument when calling a method. Extension functions behave as if they belong to the class itself, using the this keyword and calling all of its public methods directly
Extension functions do not allow you to break their encapsulation. Unlike methods defined inside a class, extension functions cannot access private or protected members
// Declare an extension function lastChar() for the String class that returns the last character of the String
// Get is an internal String method, length is an internal String member variable, and can be called directly here
fun String.lastChar(a) = get(length - 1)
// Declare an extension function doubleValue() for the Int class that returns twice its value
// This keyword represents the Int value itself
fun Int.doubleValue(a) = this * 2
Copy the code
We can then call extension functions directly as if we were calling methods declared inside the class itself
fun main(a) {
val name = "leavesC"
println("$name lastChar is: " + name.lastChar())
val age = 24
println("$age doubleValue is: " + age.doubleValue())
}
Copy the code
If you need to declare a static extension function, you must define it on the associated object so that its extension function can be called without an instance of Namer, just as you would call a Java static function
class Namer {
companion object {
val defaultName = "mike"}}fun Namer.Companion.getName(a): String {
return defaultName
}
fun main(a) {
Namer.getName()
}
Copy the code
Note that if an extension function is declared inside a class, the extension function can only be called inside the class and its subclasses, because it is declaring a non-static function that cannot be referred to externally. So extension functions are generally declared as global functions
15.2. Extended Properties
Extension functions can also be used for attributes
// Extension functions can also be used for attributes
// Add an attribute value customLen to the String class
var String.customLen: Int
get() = length
set(value) {
println("set")}fun main(a) {
val name = "leavesC"
println(name.customLen)
name.customLen = 10
println(name.customLen)
/ / 7
//set
/ / 7
}
Copy the code
15.3. Unrewritable extension functions
If you declare a View variable and assign it to an object of type Button, the call to click() will be the Button overriding method
fun main(a) {
val view: View = Button()
view.click() //Button clicked
}
open class View {
open fun click(a) = println("View clicked")}class Button : View() {
override fun click(a) = println("Button clicked")}Copy the code
This is not the case with extension functions. If the base class and subclass each define an extension function of the same name, the static type of the variable determines which extension function is called, not the runtime type of the variable
fun main(a) {
val view: View = Button()
view.longClick() //View longClicked
}
open class View {
open fun click(a) = println("View clicked")}class Button : View() {
override fun click(a) = println("Button clicked")}fun View.longClick(a) = println("View longClicked")
fun Button.longClick(a) = println("Button longClicked")
Copy the code
In addition, if a member function of a class has the same signature as an extension function, the member function is used preferentially
Extension functions don’t actually modify the original class; the underlying implementation is static imports. Extension functions can be declared in any file, so it is a common practice to put a series of related functions in a newly created file
It is important to note that extension functions do not automatically apply across the scope of the project, so if you need to use extension functions, you need to import them
15.4 Empty receiver
Extensions can be defined for nullable receiver types, even if the receiver is null, so that the developer does not have to nullate before calling the extension function, and this == NULL can be used to check if the receiver is null
fun main(a) {
var name: String? = null
name.check() //this == null
name = "leavesC"
name.check() //this ! = null
}
funString? .check(a) {
if (this= =null) {
println("this == null")
return
}
println("this ! = null")}Copy the code
Lambda expressions
Lambda expressions are essentially small pieces of code that can be passed to other functions. They can be used to extract common code structures into library functions, or they can be stored in a variable that is treated like a normal function
// All three declarations are identical because of type derivation
val plus1: (Int.Int) - >Int = { x: Int, y: Int -> x + y }
val plus2: (Int.Int) - >Int = { x, y -> x + y }
val plus3 = { x: Int, y: Int -> x + y }
println(plus3(1.2))
Copy the code
- A Lambda expression is always surrounded by curly braces, with arrows separating the argument list from the function body
- If a Lambda declares a function type, it can omit the type declaration of the function body
- If the Lambda declares parameter types and the return value supports type inference, then the function type declaration can be omitted
While it is tempting to avoid having Lambda expressions reference external variables to avoid side effects, there are cases where Lambda references external variables can simplify the calculation structure. Lambda expressions that access external environment variables are called closures, and closures can be passed as arguments or used directly. Unlike Java, closures in Kotlin can not only access external variables but also modify them
For example, suppose we need a method to calculate the sum that returns the current sum size each time a function is called. The variable that holds the current sum is not provided outside the method and is stored inside the Lambda expression
fun main(a) {
val sum = sumFunc()
println(sum(10)) / / 10
println(sum(20)) / / 30
println(sum(30)) / / 60
}
fun sumFunc(a): (Int) - >Int {
var base = 0
return fun(va: Int): Int {
base += va
return base
}
}
Copy the code
In addition, Kotlin also supports an auto-run syntax
{ va1: Int, va2: Int -> println(va1 + va2) }(10.20)
Copy the code
The most common use of Lambda expressions is to work with collections, as shown in the following example
To pull the oldest person from a list of people
data class Person(val name: String, val age: Int)
fun main(a) {
val people = listOf(Person("leavesC".24), Person("Ye".22))
println(people.maxBy { it.age }) //Person(name=leavesC, age=24)
}
Copy the code
The library function maxBy, which can be called on any collection, takes a single argument: a function that specifies the function to be used for comparison. The code in curly braces {it.age} is a Lambda expression that implements this logic
The arguments to maxBy are simplified. Here is the simplification process of maxBy
The original syntax declaration would look like this, wrapping the Lambda expression in parentheses
println(people.maxBy({ p: Person -> p.age }))
Copy the code
Kotlin has a syntactic convention to place a Lambda expression outside the parentheses if it is the last argument to a function call
println(people.maxBy() { p: Person -> p.age })
Copy the code
When the Lamdba expression is the only argument to the function, you can remove the empty parenthesis pairs in the calling code
println(people.maxBy { p: Person -> p.age })
Copy the code
Omit the declared argument types when the argument types of a Lambda expression can be derived
println(people.maxBy { p -> p.age })
Copy the code
If the current context expects a Lambda expression with only one parameter and the parameter type can be inferred, a default name is generated for that parameter: it
println(people.maxBy { it.age })
Copy the code
One significant difference between Kotlin and Java is that Lambda expressions inside functions in Kotlin are not limited to accessing function parameters and final variables; non-final variables can also be accessed and modified inside a Lambda
Access external variables from inside Lambda, which we say are captured by Lambda. When capturing a final variable, the value is stored with the Lambda code that uses the value. For non-final variables, the value is wrapped ina special wrapper, and references to this wrapper are stored with the Lambda code
var number = 0
val list = listOf(10.20.30.40)
list.forEach {
if (it > 20) {
number++
}
}
println(number) / / 2
Copy the code
A member reference is used to create a function value that calls a single method or accesses a single attribute, separated by a double colon from the class name and the name of the member (a method or an attribute) to be referenced
One use of member references is that if the block of code to be passed as an argument has already been defined as a function, instead of creating a Lambda expression that calls the function, you can pass the function (and attributes, too) directly through a member reference. In addition, member references apply to extension functions as well
data class Person(val name: String, val age: Int) {
val myAge = age
fun getPersonAge(a) = age
}
fun Person.filterAge(a) = age
fun main(a) {
val people = listOf(Person("leavesC".24), Person("Ye".22))
println(people.maxBy { it.age }) //Person(name=leavesC, age=24)
println(people.maxBy(Person::age)) //Person(name=leavesC, age=24)
println(people.maxBy(Person::myAge)) //Person(name=leavesC, age=24)
println(people.maxBy(Person::getPersonAge)) //Person(name=leavesC, age=24)
println(people.maxBy(Person::filterAge)) //Person(name=leavesC, age=24)
}
Copy the code
Never enclose parentheses after the name of a member reference, whether referring to a function or attribute
In addition, you can refer to top-level functions
fun test(a) {
println("test")}fun main(a) {
val t = ::test
}
Copy the code
Constructor references can also be used to store or defer the action of creating an instance of a class
data class Person(val name: String, val age: Int)
fun main(a) {
val createPerson = ::Person
val person = createPerson("leavesC".24)
println(person)
}
Copy the code
Extension functions in the library
The Kotlin library provides several useful extension functions, defined under the Standard file
17.1, the run
The run function takes a function argument and returns the value of the function as the return value of the run function
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T. () - >R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
Copy the code
Use cases
fun main(a) {
var nickName = "leavesC"
nickName = nickName.run {
if (isNotEmpty()) {
this
} else {
""
}
}
println(nickName)
}
Copy the code
17.2, with
The with function is not an extension function, but since it has similar functions, it is covered here. The first argument to the with function is the receiver object, and the second argument is an extension function defined on the receiver object type, so the methods and properties exposed by receiver can be called directly from within the function
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T. () - >R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
Copy the code
The with function is used to perform multiple operations on the same object without having to write out the object’s name repeatedly
For example, to build a string containing the specified content, the following calls are required
fun main(a) {
val result = StringBuilder()
result.append("leavesC")
result.append("\n")
for (letter in 'A'.'Z') {
result.append(letter)
}
println(result.toString())
}
Copy the code
The code will be much cleaner if you build with the with function instead
val result = with(StringBuilder()) {
append("leavesC")
append("\n")
for (letter in 'A'.'Z') {
append(letter)
}
toString()
}
println(result)
Copy the code
The with function is a function that takes two arguments, in this case a StringBuilder and a Lambda expression, taking advantage of the convention to place Lambda expressions outside parentheses
The return value of the with function is the result of executing the Lambda expression, which is the return value of the last expression in the Lambda, so if you change the code to something like this, because the println() method returns no value, you will print kotlin.unit
val result = with(StringBuilder()) {
append("leavesC")
append("\n")
for (letter in 'A'.'Z') {
append(letter)
}
println("Hello")
}
println(result) //kotin.Unit
Copy the code
17.3, the apply
The apply function is declared as an extension function of type T, its receiver is the recipient of the Lambda as an argument, and the final function returns this, the object itself
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T. () - >Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
Copy the code
So the only difference between apply and with is that apply always returns the object passed to it as an argument
val result = StringBuilder().apply {
append("leavesC")
append("\n")
for (letter in 'A'.'Z') {
append(letter)
}
toString()
}
println(result)
println(result.javaClass) //class java.lang.StringBuilder
Copy the code
17.4, braking
A also function takes an argument of type function, which takes the receiver itself as an argument, and ultimately returns the receiver object itself
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) - >Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
Copy the code
Use cases
fun main(a) {
val nickName = "leavesC"
val also = nickName.also {
it.length
}
println(also) //leavesC
}
Copy the code
17.5, let
A also function takes an argument of a function type, which in turn takes the receiver itself as an argument, and ultimately returns the evaluation of the function
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) - >R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)}Copy the code
Use cases
fun main(a) {
val nickName = "leavesC"
val also = nickName.let {
it.length
}
println(also) / / 7
}
Copy the code
17.6, takeIf
TakeIf accepts a function that returns a value of type bool, the recipient object itself if true, or null otherwise
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeIf(predicate: (T) - >Boolean): T? {
contract {
callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
}
return if (predicate(this)) this else null
}
Copy the code
Use cases
fun main(a) {
println(check("leavesC")) / / 7
println(check(null)) / / 0
}
fun check(name: String?).: Int {
returnname.takeIf { ! it.isNullOrBlank() }? .length ? :0
}
Copy the code
17.7, takeUnless
The judgment condition of takeUnless is opposite to that of takeIf and will not be repeated here
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeUnless(predicate: (T) - >Boolean): T? {
contract {
callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
}
return if(! predicate(this)) this else null
}
Copy the code
Function operators
18.1. Total operator
18.1.1, any
Returns true if at least one element matches the given criteria
val list = listOf(1.3.5.7.9)
println(list.any { it > 13 }) //false
println(list.any { it > 7 }) //true
Copy the code
18.1.2, all
Return true if all elements meet the given criteria
val list = listOf(1.3.5.7.9)
println(list.all { it > 13 }) //false
println(list.all { it > 0 }) //true
Copy the code
18.1.3, count
Returns the total number of elements that match the given criteria
val list = listOf(1.3.5.7.9)
println(list.count { it > 7 }) / / 1
println(list.count { it > 2 }) / / 4
Copy the code
18.1.4, a fold
Accumulates all elements in a function from the first to the last term based on an initial value
fun main(a) {
val list = listOf(1.3.5.7.9)
println(list.fold(2) { total, next->
println("$next , $total")
next + total
})
}
Copy the code
1 , 2
3 , 3
5 , 6
7 , 11
9 , 18
27
Copy the code
18.1.5, foldRight
Same as fold, but the order is from the last item to the first
val list = listOf(1.3.5.7.9)
println(list.foldRight(2) { next, total->
println("$next , $total")
next + total
})
Copy the code
9 , 2
7 , 11
5 , 18
3 , 23
1 , 26
27
Copy the code
18.1.6, forEach
val list = listOf(1.3.5.7.9)
list.forEach { print(it + 1)}/ / 246810
Copy the code
18.1.7, forEachIndexed
Similar to forEach, you can also get the index of the element
val list = listOf(1.3.5.7.9)
list.forEachIndexed { index, value -> println("$index value is $value")}0 value is 1
1 value is 3
2 value is 5
3 value is 7
4 value is 9
Copy the code
18.1.8, Max
Returns the largest item, or NULL if there is none
val list = listOf(1.3.5.7.9)
println(list.max()) / / 9
Copy the code
18.1.9, maxBy
Returns the largest item based on the given function, or NULL if there is none
val list = listOf(1.3.5.7.9)
println(list.maxBy { -it }) / / 1
Copy the code
18.1.10, min
Returns the smallest item, or null if there is none
val list = listOf(1.3.5.7.9)
println(list.min()) / / 1
Copy the code
18.1.11, minBy
Returns the smallest item based on the given function, or null if there is none
val list = listOf(1.3.5.7.9)
println(list.minBy { -it }) / / 9
Copy the code
18.1.12, none
Returns true if no element matches the given function
val list = listOf(1.3.5.7.9)
println(list.none { it > 10 }) //true
Copy the code
18.1.13, reduce
Same as fold, but without an initial value. You accumulate from the first term to the last term by a function
val list = listOf(1.3.5.7.9)
println(list.reduce { total, next ->
println("$next , $total")
total + next
})
3 , 1
5 , 4
7 , 9
9 , 16
25
Copy the code
18.1.14, reduceRight
Same as reduce, but from the last item to the first
val list = listOf(1.3.5.7.9)
println(list.reduceRight { next, total ->
println("$next , $total")
total + next
})
7 , 9
5 , 16
3 , 21
1 , 24
25
Copy the code
18.1.15, sumBy
Returns the sum of each item converted by the function
val list = listOf(1.3.5.7.9)
println(list.sumBy { it + 1 }) / / 30
Copy the code
18.2. Filter operators
18.2.1, drop
Returns a list of all elements with the first n elements removed
val list = listOf(1.3.5.7.9)
println(list.drop(2)) / / [5, 7, 9]
Copy the code
18.2.2, dropWhile
Returns a list starting from the first element that does not conform to the given function
val list = listOf(1.3.5.7.9.2)
println(list.dropWhile { it < 4 }) / / [5, 7, 9, 2]
Copy the code
18.2.3, dropLastWhile
Starting with the last item, returns the list since the beginning of the elements that do not conform to the given function
val list = listOf(10.1.3.5.7.9)
println(list.dropLastWhile { it > 4 }) / / [10, 1, 3]
Copy the code
18.2.4, filter,
Filter all elements that match the conditions of the given function
val list = listOf(1.3.5.7.9.2)
println(list.filter { it < 4 }) / / [1, 3, 2)
Copy the code
18.2.5, filterNot
Filter all elements that do not match the conditions of the given function
val list = listOf(1.3.5.7.9.2)
println(list.filterNot { it < 4 }) / / [5, 7, 9]
Copy the code
18.2.6, filterNotNull
Filter all elements that are not null
val list = listOf(1.3.5.7.9.2.null)
println(list.filterNotNull()) //[1, 3, 5, 7, 9, 2]
Copy the code
18.2.7, slice,
Filter a list of elements with the specified index
val list = listOf(1.3.5.7.9.2.null)
println(list.slice(listOf(0.3))) / / [1, 7)
Copy the code
18.2.8, take
Returns n elements starting with the first
val list = listOf(1.3.5.7.9.2.null)
println(list.take(2)) / / [1, 3]
Copy the code
18.2.9, takeLast
Returns n elements starting with the last one
val list = listOf(1.3.5.7.9.2.null)
println(list.takeLast(2)) //[2, null]
Copy the code
18.2.10, takeWhile
Returns the elements that match the conditions of the given function starting from the first.
val list = listOf(1.3.5, -1.7.9.2)
println(list.takeWhile { it > 2 }) / / []
println(list.takeWhile { it > 0 }) / / [1, 3, 5]
Copy the code
18.3. Mapping operators
18.3.1, flatMap
Iterate through all the elements, create a collection for each, and finally put all the collections into one collection
val list = listOf(1.3.5, -1.7.9.2)
println(list.flatMap { listOf(it, it + 1)})//[1, 2, 3, 4, 5, 6, -1, 0, 7, 8, 9, 10, 2, 3]
Copy the code
18.3.2, groupBy
Returns a map grouped by the given function
val list = listOf(1.3.5, -1.7.9.2)
println(list.groupBy { listOf(it) }) / / {[1] = [1], [3] = [3], [5] = [5], [1] = [1], [7] = [7], [9] = [9], [2] = [2]}
println(list.groupBy { listOf(it, it + 1)})/ / {[1, 2] = [1], [3, 4] = [3], [5, 6] = [5], [1, 0] = [1], [7, 8] = [7], [9, 10] = [9], [2, 3] = [2]}
Copy the code
18.3.3, map,
Returns a List of each element converted to a given function.
val list = listOf(1.3.5, -1.7.9.2)
println(list.map { listOf(it) }) //[[1], [3], [5], [-1], [7], [9], [2]]
println(list.map { listOf(it, it + 1)})/ / [[1, 2], [3, 4], [5, 6], [1, 0], [7, 8], [9, 10], [2, 3]]
Copy the code
18.3.4, mapIndexed
Returns a List of each element converted to the given function that contains the element index
val list = listOf(1.3.5, -1.7.9.2)
println(list.mapIndexed { index, value -> index }) //[0, 1, 2, 3, 4, 5, 6]
println(list.mapIndexed { index, value -> index * value }) //[0, 3, 10, -3, 28, 45, 12]
Copy the code
18.3.5, mapNotNull
Returns a List of each non-null element converted to the given function
val list = listOf(1.3.5, -1.7.9.null.2)
println(list.mapNotNull { it }) //[1, 3, 5, -1, 7, 9, 2]
Copy the code
18.4. Element operators
18.4.1, the contains
Returns true if the specified element can be found in the collection
val list = listOf(1.3.5, -1.7.9.null.2)
println(list.contains(3)) //true
println(list.contains(13)) //false
Copy the code
18.4.2, elementAt
Returns the given index corresponding element, if the index array bounds may be thrown IndexOutOfBoundsException
val list = listOf(1.3.5, -1.7.9.null.2)
println(list.elementAt(3)) / / 1
println(list.elementAt(6)) //null
Copy the code
11.4.3, elementAtOrElse
Returns the element corresponding to the given index, or the default value based on the given function if the index array is out of bounds
val list = listOf(1.3.5, -1.7.9.null.2)
println(list.elementAtOrElse(3, { it * 2 })) / / 1
println(list.elementAtOrElse(16, { it * 2 })) / / 32
Copy the code
18.4.4, elementAtOrNull
Returns the element corresponding to the given index, or NULL if the index array is out of bounds
val list = listOf(1.3.5, -1.7.9.null.2)
println(list.elementAtOrNull(3)) / / 1
println(list.elementAtOrNull(16)) //null
Copy the code
18.4.5, first
Returns the first element that matches the conditions of the given function
val list = listOf(1.3.5, -1.7.9.2)
println(list.first { it % 3= =0 }) / / 3
Copy the code
18.4.6, firstOrNull
Returns the first element that meets the criteria of the given function, or null if none
val list = listOf(1.3.5, -1.7.9.2)
println(list.firstOrNull { it % 3= =0 }) / / 3
println(list.firstOrNull { it % 8= =0 }) //null
Copy the code
18.4.7, indexOf
Returns the first index of the specified element, or -1 if none exists
val list = listOf(1.3.5, -1.7.9.2)
println(list.indexOf(5)) / / 2
println(list.indexOf(12)) / / 1
Copy the code
18.4.8, indexOfFirst
Returns the index of the first element that meets the criteria of the given function, or -1 if none
val list = listOf(1.3.5.1.7.9.2)
println(list.indexOfFirst { it % 2= =0 }) / / 6
println(list.indexOfFirst { it % 12= =0 }) / / 1
Copy the code
18.4.9, indexOfLast
Returns the index of the last element that meets the criteria of the given function, or -1 if none
val list = listOf(1.3.5.6.7.9.2)
println(list.indexOfLast { it % 2= =0 }) / / 6
println(list.indexOfLast { it % 12= =0 }) / / 1
Copy the code
18.4.10, last
Returns the last element that matches the conditions of the given function
val list = listOf(1.3.5.6.7.9.2)
println(list.last { it % 2= =0 }) / / 2
println(list.last { it % 3= =0 }) / / 9
Copy the code
18.4.10, lastIndexOf
Returns the last index of the specified element, or -1 if none exists
val list = listOf(1.3.2.6.7.9.2)
println(list.lastIndexOf(2)) / / 6
println(list.lastIndexOf(12)) / / 1
Copy the code
18.4.11, lastOrNull
Returns the last element that meets the criteria of the given function, or null if none
val list = listOf(1.3.2.6.7.9.2)
println(list.lastOrNull { it / 3= =3 }) / / 9
println(list.lastOrNull { it == 10 }) //null
Copy the code
18.4.12, single
Returns a single element that matches the given function, or throws an exception if there is none or more
val list = listOf(1.9.2.6.7.9.2)
println(list.single { it % 7= =0 }) / / 7
println(list.single { it == 2 }) //IllegalArgumentException
Copy the code
18.4.13, singleOrNull
Returns a single element that matches the given function, or NULL if none or more match
val list = listOf(1.9.2.6.7.9.2)
println(list.singleOrNull { it % 7= =0 }) / / 7
println(list.singleOrNull { it == 2 }) //null
Copy the code
18.5. Production operators
18.5.1, partition
Split a given set into two. The first set consists of elements that return true for each element of the original set matching the given function bar. The second set consists of elements that return false for each element of the original set matching the given function condition
val list = listOf(1.9.2.6.7.9.2)
val (list1, list2) = list.partition { it % 2= =0 }
println(list1) / / [2, 6, 2)
println(list2) / / [1, 9, 7, 9]
Copy the code
18.5.2, plus
Returns a collection containing the original collection and all elements of the given collection. Because of the name of the function, we can use the + operator
val list1 = listOf(1.9.2.6.7.9.2)
val list2 = listOf(1.2.4.6.8.10)
println(list1.plus(list2)) //[1, 9, 2, 6, 7, 9, 2, 1, 2, 4, 6, 8, 10]
println(list1 + list2) //[1, 9, 2, 6, 7, 9, 2, 1, 2, 4, 6, 8, 10]
Copy the code
18.5.3, zip,
Returns a List of pairs, each consisting of elements of the same index in two collections. The size of this returned List is determined by the smallest collection
val list1 = listOf(1.9.2.6.7.9.2)
val list2 = listOf(1.2.4.6.8.10)
val list3 = list1.zip(list2)
println(list3.javaClass)
println(list3.get(0).javaClass)
println("${list3.get(0).first} , ${list3.get(0).second}")
list3.forEach { println(it) }
Copy the code
class java.util.ArrayList
class kotlin.Pair
1 , 1
(1.1)
(9.2)
(2.4)
(6.6)
(7.8)
(9.10)
Copy the code
18.5.4, unzip
Generate a pair containing a List from a List containing a pair
val list1 = listOf(Pair("leavesC".1), Pair("leavesC_2".2), Pair("leavesC_3".3))
val list2 = list1.unzip()
println(list2.javaClass)
println(list2.first)
println(list2.second)
Copy the code
class kotlin.Pair
[leavesC, leavesC_2, leavesC_3]
[1.2.3]
Copy the code
18.6. Sequential operators
18.6.1, reverse
Returns a list in the reverse order of the specified list
val list1 = listOf(Pair("leavesC".1), Pair("leavesC_2".2), Pair("leavesC_3".3))
val list2 = list1.reversed()
println(list2) //[(leavesC_3, 3), (leavesC_2, 2), (leavesC, 1)]
Copy the code
18.6.2, sort
Returns a naturally sorted list
val list1 = listOf(2.4.1.9.5.10)
val list2 = list1.sorted()
println(list2) //[1, 2, 4, 5, 9, 10]
val list3 = listOf("a"."c"."ab"."b"."cdd"."cda")
val list4 = list3.sorted()
println(list4) //[a, ab, b, c, cda, cdd]
Copy the code
18.6.3, sortBy
Returns a list sorted by the specified function
val list1 = listOf(2.4.1.9.5.10)
val list2 = list1.sortedBy { it - 3 }
println(list2) //[1, 2, 4, 5, 9, 10]
Copy the code
18.6.4, sortDescending
Returns a descending sorted List
val list1 = listOf(2.4.1.9.5.10)
val list2 = list1.sortedDescending()
println(list2) //[10, 9, 5, 4, 2, 1]
Copy the code
18.6.5, sortDescendingBy
Returns a descending list sorted by the specified function
val list1 = listOf(2.4.1.9.5.10)
val list2 = list1.sortedByDescending { it % 2 }
println(list2) //[1, 9, 5, 2, 4, 10]
Copy the code
19. Anomalies
The basic form of exception handling in Kotlin is similar to Java
fun compute(index: Int): Boolean {
if (index !in 0.10.) {
throw IllegalArgumentException("Parameter error")}return true
}
Copy the code
Unlike In Java, the Throw structure in Kotlin is an expression that can be used as part of another expression
For example, in the following example, if the condition is not met, an exception will be thrown so that the status variable will not be initialized
val status = if (index in 0.10.) index else throw IllegalArgumentException("Parameter error")
Copy the code
In addition, checked exceptions must be handled explicitly in Java, either by catching the exception in a try/catch statement or by throwing it to the caller. Kotlin does not distinguish between checked and unchecked exceptions, and may or may not handle exceptions without specifying exceptions thrown by the function
In Kotlin, the try keyword introduces an expression so that the value of the expression can be assigned to a variable. If a try block executes properly, the last expression in the block is the result, and if an exception is caught, the last expression in the corresponding catch block is the result
The following example returns null if the expression wrapped by the try expression throws an exception, and true otherwise
fun main(a) {
compute(5) //fun end : true
compute(100) //fun end : null
}
fun compute(index: Int) {
val status = try {
if (index in 0.10.) true else throw IllegalArgumentException("Parameter error")}catch (e: Exception) {
null
}
println("fun end : " + status)
}
Copy the code
However, if you end compute with a return in a catch statement, there is no output
fun main(a) {
compute(5) //fun end : true
compute(100) // There is no output
}
fun compute(index: Int) {
val status = try {
if (index in 0.10.) true else throw IllegalArgumentException("Parameter error")}catch (e: Exception) {
return
}
println("fun end : " + status)
}
Copy the code
Operator overloading
Kotlin allows you to provide predefined implementations of operators for types with fixed symbolic representations (such as + and *) and fixed priorities, and operator overloading allows you to map the behavior of the operators to specified methods. To implement such an operator, you need to provide a member or extension function with a fixed name for the class, and the corresponding overloaded operator functions need to be marked with the operator modifier
20.1. Unary operators
The operator | function |
---|---|
+a | a.unaryPlus() |
-a | a.unaryMinus() |
! a | a.not() |
a++ | a.inc() |
a– | a.dec() |
20.2 binary operators
The operator | function |
---|---|
a + b | a.plus(b) |
a – b | a.minus(b) |
a * b | a.times(b) |
a / b | a.div(b) |
a % b | a.rem(b) |
a.. b | a.rangeTo(b) |
a in b | b.contains(a) |
a ! in b | ! b.contains(a) |
a += b | a.plusAssign(b) |
a -= b | a.minusAssign(b) |
a *= b | a.timesAssign(b) |
a /= b | a.divAssign(b) |
a %= b | a.remAssign(b) |
20.3. Array operators
The operator | function |
---|---|
a[i] | a.get(i) |
a[i, j] | a.get(i, j) |
a[i_1, …, i_n] | a.get(i_1, … , i_n) |
a[i] = b | a.set(i, b) |
a[i, j] = b | a.set(i, j, b) |
a[i_1, …, i_n] = b | a.set(i_1, … , i_n, b) |
20.4. The equal operator
The operator | function |
---|---|
a == b | a? .equals(b) ? : b === null |
a ! = b | ! (a? .equals(b) ? : b === null) |
The equality operator is a bit different, with more complex transformations to achieve proper equality checks, because to get an exact function structure comparison, it’s not just the specified name
Methods must be implemented exactly as follows:
operator fun equals(other: Any?).: Boolean
Copy the code
The operators === and! == is used for identity checks (they are Java == and! =), and they cannot be overloaded
20.5. Comparison operators
The operator | function |
---|---|
a > b | a.compareTo(b) > 0 |
a < b | a.compareTo(b) < 0 |
a >= b | a.compareTo(b) >= 0 |
a <= b | a.compareTo(b) <= 0 |
All comparisons are converted to calls to compareTo, which need to return an Int
20.6. Function calls
methods | call |
---|---|
a() | a.invoke() |
a(i) | a.invoke(i) |
a(i, j) | a.invoke(i, j) |
a(i_1, … , i_n) | a.invoke(i_1, … , i_n) |
Example 20.7,
Let’s look at some examples
data class Point(val x: Int.val y: Int) {
//+Point
operator fun unaryPlus(a) = Point(+x, +y)
//Point++ / ++Point
operator fun inc(a) = Point(x + 1, y + 1)
//Point + Point
operator fun plus(point: Point) = Point(x + point.x, y + point.y)
//Point + Int
operator fun plus(value: Int) = Point(x + value, y + value)
//Point[index]
operator fun get(index: Int): Int {
return when (index) {
0 -> x
1 -> y
else -> throw IndexOutOfBoundsException("Invalid index")}}//Point(index)
operator fun invoke(index: Int) = when (index) {
0 -> x
1 -> y
else -> throw IndexOutOfBoundsException("Invalid index")}}Copy the code
fun main(a) {
//+Point(x=10, y=-20) = Point(x=10, y=-20)
println("+${Point(10.- 20)} = ${+Point(10.- 20)}")
//Point(x=10, y=-20)++ = Point(x=10, y=-20)
var point = Point(10, -20)
println("${Point(10.- 20)}+ + =${point++}")
//++Point(x=10, y=-20) = Point(x=11, y=-19)
point = Point(10, -20)
println("+ +${Point(10.- 20)} = ${++point}")
//Point(x=10, y=-20) + Point(x=10, y=-20) = Point(x=20, y=-40)
println("${Point(10.- 20)} + ${Point(10.- 20)} = ${Point(10.- 20) + Point(10.- 20)}")
//Point(x=10, y=-20) + 5 = Point(x=15, y=-15)
println("${Point(10.- 20)} + The ${5} = ${Point(10.- 20) + 5}")
point = Point(10, -20)
//point[0] value is: 10
println("point[0] value is: ${point[0]}")
//point[1] value is: -20
println("point[1] value is: ${point[1]}")
//point(0) values is: 10
println("point(0) values is: ${point(0)}")}Copy the code
Infix call and destruct declaration
21.1 Infix Invocation
You can create a Map variable in the following form
fun main(a) {
val maps = mapOf(1 to "leavesC".2 to "ye".3 to "czy")
maps.forEach { key, value -> println("key is : $key , value is : $value")}}Copy the code
Function calls that use “to” to declare the mapping between a map’s key and value are called infix calls
The kotlin library declares the to function as follows, which exists as an extension function and is a generic function. The return value Pair is eventually passed to Map through the destruct declaration
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
Copy the code
Infix calls can only be used with functions that take one argument, whether ordinary or extended. Infix symbols need to be marked with the infix modifier
fun main(a) {
val pair = 10 test "leavesC"
val pair2 = 1.2 test 20
println(pair2.javaClass) //class kotlin.Pair
}
infix fun Any.test(other: Any) = Pair(this, other)
Copy the code
The mapOf function can accept an indefinite number of objects of type Pair, so we can also create a map variable with the custom infix caller test
public fun <K, V> mapOf(vararg pairs: Pair<K, V>): Map<K, V> =
if (pairs.size > 0) pairs.toMap(LinkedHashMap(mapCapacity(pairs.size))) else emptyMap()
Copy the code
val map = mapOf(10 test "leavesC".20 test "hello")
Copy the code
21.2 Deconstruct the statement
Sometimes there is a need to break an object into multiple variables, a syntax called deconstruction declarations in Kotlin
For example, the following example structures the Person variable as two new variables, name and age, and can use them independently
data class Person(val name: String, val age: Int)
fun main(a) {
val (name, age) = Person("leavesC".24)
println("Name: $name , age: $age")
//Name: leavesC , age: 24
}
Copy the code
A destruct declaration is compiled into the following code:
val name = person.component1()
val age = person.component2()
Copy the code
The component1() and Component2 () functions are another example of the convention principle widely used in Kotlin. Any expression can appear on the right side of a destruct declaration, as long as the required number of Component functions can be called on it
Note that the componentN() functions need to be marked with the operator keyword to allow them to be used in destruct declarations
For data classes, the function componentN() is generated automatically, whereas for non-data classes, we need to declare the function ourselves manually in order to use destruct declarations
class Point(val x: Int.val y: Int) {
operator fun component1(a) = x
operator fun component2(a) = y
}
fun main(a) {
val point = Point(100.200)
val (x, y) = point
println("x: $x , y: $y")
//x: 100 , y: 200
}
Copy the code
If we need to return two or more values from a function, this is a convenient time to use destruct declarations
The standard Pair class is used to wrap the data to be passed, but you can also customize the data classes
fun computer(a): Pair<String, Int> {
// Various calculations
return Pair("leavesC".24)}fun main(a) {
val (name, age) = computer()
println("Name: $name , age: $age")}Copy the code
Also, destruct declarations can be used in for loops
val list = listOf(Person("leavesC".24), Person("leavesC".25))
for ((name, age) in list) {
println("Name: $name , age: $age")}Copy the code
The same is true for traversing a map
val map = mapOf("leavesC" to 24."ye" to 25)
for ((name, age) in map) {
println("Name: $name , age: $age")}Copy the code
The same applies to lambda expressions
val map = mapOf("leavesC" to 24."ye" to 25)
map.mapKeys { (key, value) -> println("key : $key , value : $value")}Copy the code
If a variable is not needed in a destruct declaration, its name can be replaced with an underscore, and the corresponding componentN() operator function is not called
val map = mapOf("leavesC" to 24."ye" to 25)
for ((_, age) in map) {
println("age: $age")}Copy the code
22. The Object keyword
22.1 Object declaration
In Kotlin’s world, the singleton pattern in Java can be implemented through object declarations, which combine class declarations with single-instance declarations of that class. Like classes, an object declaration can contain declarations of attributes, methods, initialization blocks, and so on, and can inherit classes and implementation interfaces. The only thing that is not allowed is constructors
Unlike instances of ordinary classes, object declarations are created immediately when they are defined, and constructors do not need to be called elsewhere in the code, so it makes no sense to define constructors for object declarations
interface Fly {
fun fly(a)
}
open class Eat {
fun eat(a) {
println("eat")}}object Animal : Eat(), Fly {
override fun fly(a) {
println("fly")}}fun main(a) {
Animal.fly()
Animal.eat()
}
Copy the code
The object declaration in Kotlin is compiled into a class that holds its single INSTANCE through a static field, which is always named INSTANCE
For example, for the following two object declarations in Kotlin
class Test {
object SingleClass {
val names = arrayListOf<String>()
}
object SingleClass2 {
val names = arrayListOf<String>()
}
}
Copy the code
Access both objects in Java code
public static void main(String[] args) {
Test.SingleClass.INSTANCE.getNames();
Test.SingleClass2.INSTANCE.getNames();
}
Copy the code
22.2 Associated objects
If you need a function that can be called without an instance of the class but needs to access the inside of the class (similar to static variables/functions in Java), you can write it as a declared member of the object in that class
With the companion keyword, you gain the ability to access the methods and properties of an object by its container class name, without explicitly specifying the object’s name
class Test {
companion object {
const val NAME = ""
fun testFun(a){}}}fun main(a) {
Test.NAME
Test.testFun()
}
Copy the code
22.2.1 Factory mode
The factory pattern can be implemented using associated objects
private class User private constructor(val name: String) {
companion object {
fun newById(id: Int) = User(id.toString())
fun newByDouble(double: Double) = User(double.toString())
}
}
fun main(a) {
// Constructor is private and cannot be created
//val user1 = User("leavesC")
val user2 = User.newById(10)
val user3 = User.newByDouble(1.3)}Copy the code
22.2.2. Specify a name
Companion objects can either be named or use their default name Companion, and when referring to Companion objects, you are free to append the Companion object name to the class name
If you use its default name Companion (there is no custom name), then both of the following references are equivalent
val user2 = User.Companion.newById(10)
val user3 = User.newByDouble(1.3)
Copy the code
If a custom name is declared for the associated object, the reference is the same
private class User private constructor(val name: String) {
companion object UserLoader {
fun newById(id: Int) = User(id.toString())
fun newByDouble(double: Double) = User(double.toString())
}
}
fun main(a) {
// Constructor is private and cannot be created
//val user1 = User("leavesC")
val user2 = User.UserLoader.newById(10)
val user3 = User.newByDouble(1.3)}Copy the code
22.2.3 Interface implementation
A companion object can also implement the interface, and the name of the containing class can be used directly as an object instance that implements the interface
private class User private constructor(val name: String) {
companion object UserLoader : Runnable {
override fun run(a){}}}fun newThread(runnable: Runnable) = Thread(runnable)
fun main(a) {
//User is treated directly as an instance of Runnable
val thread = newThread(User)
val thread2 = newThread(User.UserLoader)
}
Copy the code
22.3 object expressions
Object can be used to declare anonymous objects, which can be used instead of anonymous inner classes in Java, and code in an object expression can access and modify external variables that are not final
fun newThread(runnable: Runnable) = Thread(runnable)
fun main(a) {
var count = 0
val thread = newThread(object : Runnable {
override fun run(a) {
count++
}
})
}
Copy the code
23. Commission
23.1. Delegation Mode
The delegate pattern is a basic design pattern in which two objects participate in processing the same request, and the receiving object delegates the request to the other object. Kotlin natively supports the delegate pattern, which can be implemented with zero boilerplate code and with the by keyword
interface Printer {
fun print(a)
}
class DefaultPrinter : Printer {
override fun print(a) {
println("DefaultPrinter print")}}class CustomPrinter(val printer: Printer) : Printer by printer
fun main(a) {
val printer = CustomPrinter(DefaultPrinter())
printer.print() //DefaultPrinter print
}
Copy the code
The BY clause of CustomPrinter means that the printer variable will be stored in CustomPrinter, and that the compiler will implicitly generate all abstract methods of the Printer interface for CustomPrinter, And forward the invocation operations of these methods to Printer
In addition, CustomPrinter can decide to implement some or all of its own methods, but the overridden member is not called in the member of the delegate object, which can only access its own implementation of the interface member
interface Printer {
val message: String
fun print(a)
fun reprint(a)
}
class DefaultPrinter : Printer {
override val message: String = "DefaultPrinter message"
override fun print(a) {
println(message)
}
override fun reprint(a) {
println("DefaultPrinter reprint")}}class CustomPrinter(val printer: Printer) : Printer by printer {
override val message: String = "CustomPrinter message"
override fun reprint(a) {
println("CustomPrinter reprint")}}fun main(a) {
val printer = CustomPrinter(DefaultPrinter())
printer.print() //DefaultPrinter message
printer.reprint() //CustomPrinter reprint
}
Copy the code
23.2 Attribute delegate
Kotlin supports delegating access to an attribute to another object using the following syntax:
val/var< attribute name >: < type >by< expression >Copy the code
The attribute delegate does not have to implement any interface, but provides a getValue() and setValue() method (for the var attribute) to which the get and set operations on an attribute are delegated
class Delegate {
// The first argument represents the delegate object, and the second argument represents the delegate object itself
operator fun getValue(thisRef: Any? , property:KProperty< * >): String {
}
// The first argument represents the delegate object, the second argument represents a description of the delegate object itself, and the third argument is the value to be assigned
operator fun setValue(thisRef: Any? , property:KProperty<*>, value: String){}}Copy the code
Take a look at the small example below, where the output values show when each method is called
package test
import kotlin.reflect.KProperty
class Delegate {
private var message: String? = null
operator fun getValue(thisRef: Any? , property:KProperty< * >): String {
println("${thisRef? .javaClass? .name}, thank you for delegating '${property.name}' to me!")
returnmessage ? :"null value"
}
operator fun setValue(thisRef: Any? , property:KProperty<*>, value: String) {
println("$value has been assigned to '${property.name}' in ${thisRef? .javaClass? .name}.")
message = value
}
}
class Example {
var strValue: String by Delegate()
}
fun main(a) {
val example = Example()
println(example.strValue)
example.strValue = "leaveC"
println(example.strValue)
// test.Example, thank you for delegating 'strValue' to me!
// null value
// leaveC has been assigned to 'strValue' in test.Example.
// test.Example, thank you for delegating 'strValue' to me!
// leaveC
}
Copy the code
23.3. Delay properties
Lazy () is a function that takes a lambda and returns an instance of lazy < T > that can be used as a delegate to implement the delay property. The first call to get() executes the lambda expression passed to the lazy() function and records the result, Subsequent calls to get() simply return the result of the record
class Example {
val lazyValue1: String by lazy {
println("lazyValue1 computed!")
"Hello"
}
val lazyValue2: String by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
println("lazyValue2 computed!")
computeLazyValue()
}
private fun computeLazyValue(a) = "leavesC"
}
fun main(a) {
val example = Example()
println(example.lazyValue1) //lazyValue1 computed! Hello
println(example.lazyValue1) //Hello
println(example.lazyValue2) //lazyValue2 computed! leavesC
}
Copy the code
By default, the lazy attribute is evaluated with synchronous lock (synchronized), namely with LazyThreadSafetyMode. Synchronized parameter, the value is only allowed at this time the same time there is only one thread on the initialization, And all threads will see the same initialization value. If initialization synchronous entrusted by the lock is not required, if allows multiple threads to perform at the same time, then can be LazyThreadSafetyMode. PUBLICATION passed as a parameter to lazy () function. Initialization and if you are sure to happen in a single thread, you can use LazyThreadSafetyMode. NONE pattern, at the moment there is no thread safety guarantee and related resources
23.4 Observable attributes
Delegates. Observable () accepts two parameters: the initial value and the call-back function when the property value is modified. The callback function is called when a value is assigned to a property and takes three arguments: the assigned property, the old value, and the new value
fun main(a) {
val example = Example()
example.age = 24 //kProperty.name: age , oldValue: -100 , newValue: 24
example.age = 27 //kProperty.name: age , oldValue: 24 , newValue: 27
}
class Example {
var age: Int by Delegates.observable(-100) { kProperty: KProperty<*>, oldValue: Int, newValue: Int ->
println("kProperty.name: ${kProperty.name} , oldValue: $oldValue , newValue: $newValue")}}Copy the code
If you want to intercept an assignment and decide whether to reject it, you can use the vetoable() function to decide whether to intercept by returning a Boolean before the attribute is assigned a new value
fun main(a) {
val example = Example()
example.age = 24 //kProperty.name: age , oldValue: -100 , newValue: 24
example.age = -10 //kProperty.name: age , oldValue: 24 , newValue: -10
example.age = 30 // kproperty. name: age, oldValue: 24, newValue: 30
}
class Example {
var age: Int by Delegates.vetoable(-100) { kProperty: KProperty<*>, oldValue: Int, newValue: Int ->
println("kProperty.name: ${kProperty.name} , oldValue: $oldValue , newValue: $newValue")
age <= 0 // Return true to intercept the assignment}}Copy the code
23.5. Store attributes in a map
You can store attribute values in a Map map and delegate attribute access to the map
fun main(a) {
val student = Student(
mapOf(
"name" to "leavesC"."age" to 24
)
)
println(student.name)
println(student.age)
}
class Student(valmap: Map<String, Any? >) {val name: String by map
val age: Int by map
}
Copy the code
In the example above, the attributes name and age are immutable (val), so the map is also of type MAP and not MutableMap (which can be changed after assignment), so in order to support the var attribute, You can replace a read-only Map with a MutableMap
23.6. Local delegate properties
Local variables can be declared as delegate properties
class Printer {
fun print(a) {
println("temp.Printer print")}}fun getPrinter(a): Printer {
println("temp.Printer getPrinter")
return Printer()
}
// Local delegate
fun example(getPrinter: () -> Printer) {
val lPrinter by lazy(getPrinter)
val valid = true
if (valid) {
lPrinter.print()
}
}
fun main(a) {
example { getPrinter() }
//temp.Printer getPrinter
//temp.Printer print
}
Copy the code
The delegate variable is initialized only on the first access, so the getPrinter() method will not be called if valid is false
Xxiv. Notes
Annotations are a way to attach metadata to code elements so that the attached metadata can be accessed by the relevant source code tools either in a compiled class file or at runtime
The syntax format of the annotations is as follows:
annotation class AnnotationName(a)Copy the code
Additional attributes for annotations can be specified by annotating annotation classes with meta-annotations:
- @target Specifies the scope of the annotation annotation (class, function, attribute, etc.)
- @Retention specifies whether the annotation is to be stored in the compiled class file, and if so, the annotation value can be retrieved at run time through reflection
- Repeatable indicates that the same annotation is allowed to be used multiple times on a single element
- @Mustbedocumented Specifies that the annotation is part of the public API and should be included in the signature of the class or method displayed in the generated API documentation
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
@Repeatable
@MustBeDocumented
annotation class AnnotationName(a)Copy the code
An annotation can declare a constructor that contains arguments
annotation class OnClick(val viewId: Long)
Copy the code
The allowed parameter types are:
- Native data types, corresponding to Java native int, long, char, and so on
- string
- Class object
- The enumeration
- Other annotations
- An array of the above types
Annotation parameters cannot contain nullable types because the JVM does not support storing NULL as the value of an annotation attribute
Look at an example of getting an annotation value at run time
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class OnClick(val viewId: Long)
class AnnotationsTest {
@OnClick(200300)
fun onClickButton(a) {
println("Clicked")}}fun main(a) {
val annotationsTest = AnnotationsTest()
for (method in annotationsTest.javaClass.methods) {
for (annotation in method.annotations) {
if (annotation is OnClick) {
println("method name: " + method.name) //method name: onClickButton
println("OnClick viewId: " + annotation.viewId) //OnClick viewId: 200300}}}}Copy the code