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
  1. A Lambda expression is always surrounded by curly braces, with arrows separating the argument list from the function body
  2. If a Lambda declares a function type, it can omit the type declaration of the function body
  3. 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