Kotlin学习笔记

254 阅读4分钟

文章目录

Kotlin概述

  • 一种在Java虚拟机上运行的静态类型编程语言
  • 可以和Java代码相互操作
  • 容易在Android项目中代替Java或者同Java一起使用
  • 在2019年的Google I/O大会上Kotlin被选为Android开发者首选语言

Kotlin特点

  • 简洁易用
  • 安全
  • 互操作性
  • 工具友好

Kotlin设计理念

  • Readability
  • Reuse
  • Interoperablity
  • Safety
  • Tooling

Kotlin构建流程

在这里插入图片描述

Kotlin必备基础

Kotlin基本数据类型

在这里插入图片描述
在这里插入图片描述

Kotlin的数组

在这里插入图片描述

生成数组的方式

/**
 * 数组
 */
fun arrayType() {
    //arrayOf
    val array = arrayOf(1, 2, 3)

    //arrayOfNulls
    val array1 = arrayOfNulls<Int>(3)
    array1[0] = 4
    array1[1] = 5
    array1[2] = 6

    //Array(5)的构造函数
    val array2 = Array(5) { i -> (i * i).toString() }

//    intArrayOf(),doubleArrayOf()
    val x: IntArray = intArrayOf(1, 2, 3)
    println("x[0] + x[1] = ${x[0] + x[1]}")

    // 大小为 5、值为 [0, 0, 0, 0, 0] 的整型数组
    val array3 = IntArray(5)

    // 例如:用常量初始化数组中的值
    // 大小为 5、值为 [42, 42, 42, 42, 42] 的整型数组
    val array4 = IntArray(5) { 42 }

    // 例如:使用 lambda 表达式初始化数组中的值
    // 大小为 5、值为 [0, 1, 2, 3, 4] 的整型数组(值初始化为其索引值)
    val array5 = IntArray(5) { it * 1 }

    println(array5[4])

遍历数组的5种方式

    /****遍历数组的常用5中方式****/
    //数组遍历
    for (item in array) {
        println(item)
    }

    //带索引遍历数组
    for (i in array.indices) {
        println("$i -> ${array[i]}")
    }

    // 遍历元素(带索引)
    for ((index, item) in array.withIndex()) {
        println("$index -> $item ")
    }
    //forEach遍历数组
    array.forEach { println(it) }

    // forEach增强版
    array.forEachIndexed { index, item ->
        println("$index -> $item ")
    }
}

Kotlin的集合

在这里插入图片描述

  • 集合的可变性与不可变性
    在Kotlin存在两种意义上的集合,一种是可以修改的一种是不可修改的
    在这里插入图片描述
    每个不可变集合都有对应的可变集合,也就是以mutable为前缀的集合。
  • 测试代码
/**
 * 集合
 */
fun collectionType() {
    //不可变集合
    val stringList = listOf("one", "two", "one")
    println(stringList)

    val stringSet = setOf("one", "two", "one")
    println(stringSet)

    //可变集合

    val numbers = mutableListOf(1, 2, 3, 4)
    numbers.add(5)
    numbers.removeAt(1)
    numbers[0] = 0
    println(numbers)


    val hello = mutableSetOf("H", "e", "l", "l", "o")//自动过滤重复元素
    hello.remove("o")
    println(hello)

//集合的加减操作
    hello += setOf("w", "o", "r", "l", "d")
    println(hello)
    /**Map<K, V> 不是 Collection 接口的继承者;但是它也是 Kotlin 的一种集合类型**/
    val numberMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 4, "key5" to 5)
    println("All keys:${numberMap.keys}")
    println("All values:${numberMap.values}")

    if ("key2" in numberMap) println("Value by key key2:${numberMap["key2"]}")
    if (1 in numberMap.values) println("1 is in the map")
    if (numberMap.containsValue(1)) println("1 is in the map")

    /**
     * Q1:两个具有相同键值对,但顺序不同的map相等吗?为什么?
     * 无论键值对的顺序如何,包含相同键值对的两个 Map 是相等的
     * 具体原因可参看equals源码
     */
    val anotherMap = mapOf("key2" to 2, "key1" to 1, "key3" to 3, "key4" to 4, "key5" to 5)
    println("anotherMap == numberMap:${anotherMap == numberMap}")
    anotherMap.equals(numberMap)
    /**
     * Q2:两个具有相同元素,但顺序不同的list相等吗?为什么?
     */
}

集合排序

/**
 * 集合排序
 */
fun collectionSort() {
    val number3 = mutableListOf(1, 2, 3, 4)
    //随机排序
    number3.shuffle()
    println(number3)

    number3.sort()//从小到大
    number3.sortDescending()//从大到小
    println(number3)

    //条件排序

    data class Language(var name: String, var score: Int)

    val languageList: MutableList<Language> = mutableListOf()
    languageList.add(Language("Java", 80))
    languageList.add(Language("Kotlin", 90))
    languageList.add(Language("Dart", 99))
    languageList.add(Language("C", 80))
    //使用sortBy进行排序,适合单条件排序
    languageList.sortBy { it.score }
    println(languageList)

    //使用sortWith进行排序,适合多条件排序
    languageList.sortWith(compareBy({
        //it变量是lambda中的隐式参数
        it.score
    }, { it.name }))
    println(languageList)
}

Kotlin方法与Lambda表达式

  • 方法声明
    在这里插入图片描述
  • 测试代码
/**---------方法声明---------**/
fun functionLearn(days: Int): Boolean {
    Person().test1()
    Person.test2()
    println("NumUtil.double(2):${NumUtil.double(2)}")
    println("double(3):${double(3)}")
    return days > 100
}


class Person {
    /**
     * 成员方法
     */
    fun test1() {
        println("成员方法")
    }

    /**
     * 类方法:Kotlin中并没有static关键字,不过我们可以借助companion object 来实现类方法的目的
     */
    companion object {
        fun test2() {
            println("companion object 实现的类方法")
        }
    }
}

/**
 * 整个静态类
 */
object NumUtil {
    fun double(num: Int): Int {
        return num * 2
    }
}
/**
 * 全局静态直接新建一个 Kotlin file 然后定义一些常量、方法
 */

/**
 * 单表达式方法,当方法返回单个表达式时,可以省略花括号并且在 = 符号之后指定代码体即可:
 */
fun double(x: Int): Int = x * 2

/**
 * 当返回值类型可由编译器推断时,显式声明返回类型是可选的:
 */

fun double2(x: Int) = x * 2

/**---------方法参数---------**/

/**
 * 默认值,方法参数可以有默认值,当省略相应的参数时使用默认值。与其Java相比,这可以减少重载数量:
 */
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) { /*……*/
}

/**
 * 如果一个默认参数在一个无默认值的参数之前,那么该默认值只能通过使用具名参数调用该方法来使用:
 */
fun foo(bar: Int = 0, baz: Int) {}

fun defaultValue() {
    foo(baz = 1) // 使用默认值 bar = 0
    foo(1) { println("hello") }// 使用默认值 baz = 1
    foo(qux = { println("hello") })// 使用两个默认值 bar = 0 与 baz = 1
    foo { println("hello") }
}

/**
 * 如果在默认参数之后的最后一个参数是 lambda 表达式,那么它既可以作为具名参数在括号内传入,也可以在括号外传入
 */
fun foo(bar: Int = 0, baz: Int = 1, qux: () -> Unit) {} // 使用两个默认值 bar = 0 与 baz = 1

/**
 * 可变数量的参数(Varargs)
 */

fun append(vararg str: Char): String {
    val result = StringBuffer()
    for (char in str) {
        result.append(char)
    }
    return result.toString()
}

fun testVarargs() {
    println(append('h', 'e', 'l', 'l', 'o'))
    val world = charArrayOf('w', 'o', 'r', 'l', 'd')
    val result = append('h', 'e', 'l', 'l', 'o', ' ', *world)
    println(result)
}

/**---------方法作用域---------**/

/**
 * 局部方法
 */
fun magic(): Int {
    fun foo(v: Int): Int {
        return v * v
    }

    val v1 = (0..100).random()

    return foo(v1)
}

Lambda表达式

  • Lambda表达式的特点
    在这里插入图片描述
  • Lambda语法
    在这里插入图片描述
  • 测试代码
/**---------Lambda表达式---------**/

fun bindListener(view: View) {
    view.setOnClickListener { v ->
        Toast.makeText(v.context, "Lambda简洁之道", Toast.LENGTH_LONG).show()
    }
}

/**
 * 无参数的情况
 */
// 原方法
fun test() {
    println("无参数")
}

// lambda代码
val test1 = { println("无参数") }


/**
 * 有参数的情况,这里举例一个两个参数的例子,目的只为大家演示
 */

// 源代码
fun test2(a: Int, b: Int): Int {
    return a + b
}

// lambda
val test3: (Int, Int) -> Int = { a, b -> a + b }

// 或者
val test4 = { a: Int, b: Int -> a + b }


/**
 * lambda表达式作为方法中的参数的时候
 */

// 源代码
fun test(a: Int, b: Int): Int {
    return a + b
}

fun sum(num1: Int, num2: Int): Int {
    return num1 + num2
}

// lambda
fun test(a: Int, b: (num1: Int, num2: Int) -> Int): Int {
    return a + b(3, 5)
}
  • 认识it
    在这里插入图片描述
  • 测试代码
//如何使用it
fun test1() {
    // 这里举例一个语言自带的一个高阶方法filter,此方法的作用是过滤掉不满足条件的值。
    val arr = arrayOf(1, 3, 5, 7, 9)
    // 过滤掉数组中元素小于2的元素,取其第一个打印。这里的it就表示每一个元素。
    println(arr.filter { it < 5 }.component1())

    //结合上文的
    testClosure(1)(2) {
        println(it)
    }
}
  • 使用 _

在这里插入图片描述

Kotlin方法进阶

  • 高阶函数
  1. 函数作为参数
  2. 函数作为返回值
  • 实战:实现一个能够对集合元素进行求和的高阶函数,并且每遍历一个集合元素要有回调
/**
 * 高阶函数-函数作为参数
 **/
//这是一个求List<Int>元素和的扩展,它因为接受一个函数类型的callback参数而被成为高阶函数
fun List<Int>.sum(callback: (Int) -> Unit): Int {
    var result = 0
    for (v in this) {
        result += v
        callback(v)
    }
    return result;
}
  • 实战: 实现一个能够对集合元素进行求和的高阶函数,并且返回一个声明为(scale:Int) -> Float的函数
/**
 * 高阶函数-函数作为返回值
 **/
//这是一个求List<String>元素和的扩展,它返回一个(scale: Int) -> Float 函数而被成为高阶函数
fun List<String>.toIntSum(): (scale: Int) -> Float {
    println("第一层函数")
    return fun(scale): Float {
        var result = 0f
        for (v in this) {
            result += v.toInt() * scale
        }
        return result
    }
}

闭包(Closure)

  • 闭包可以理解为能够读取其他方法内部变量的方法

  • 闭包是将方法内部和外部连接起来的桥梁
    闭包的特性:

    1. 方法可以作为另一个方法的返回值或参数,还可以作为一个变量的值
    2. 方法可以嵌套定义,即在一个方法内部定义另一个方法

闭包的好处
1. 加强模块化
2. 抽象
3. 灵活
4. 简化代码

  • 实战
    在这里插入图片描述
  • 代码

/**
 * 闭包,testClosure接收一个Int类型的参数,返回一个带有如下参数的方法`(Int, (Int) -> Unit)`
 * ,该方法第一个参数是Int类型,第二个参数是一个接收Int类型参数的方法;
 * testClosure也是高阶方法
 */
fun testClosure(v1: Int): (v2: Int, (Int) -> Unit) -> Unit {
    return fun(v2: Int, printer: (Int) -> Unit) {//匿名方法
        printer(v1 + v2)
    }
    //难度较高,慢慢理解,参考lambda
}
  • 解构声明

/**
 * 解构声明
 */

data class Result(val msg: String, val code: Int)

fun testDeco() {
    var result = Result("success", 0)
    result = result()
    val (msg, code) = result
    println("msg:${msg}")
    println("code:${code}")
}

fun result(): Result {
    return Result("good", 1)
}
  • 函数字面值
/**
 * 方法字面值
 */
//定义了一个变量 tmp,而该变量的类型就是 (Int) -> Boolean
var tmp: ((Int) -> Boolean)? = null

fun literal() {
    // { num -> (num > 10) }即是一个方法字面值
    tmp = { num -> (num > 10) }
}

Kotlin类与接口

在这里插入图片描述

  • 类与接口
    在这里插入图片描述
  • 测试代码

fun main() {
//    InitDemo("test")
//    Animal(22)// Kotlin 中没有“new”关键字
    testDataUtil()
}
/**---------构造方法---------**/
/**
 * 主构造方法
 */
class KotlinClass constructor(name: String) { /*……*/ }

//没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字
class KotlinClass1(name: String) { /*……*/ }

class InitDemo(name: String) {
    /**
     * 关于:.also(::println)的理解
     * ::表示创建成员引用或类引用,通过它来传递println方法来作为also的参数
     * 那also又是什么呢?源码跟进 -> Kotlin扩展
     */
    val firstProp = "First Prop: $name".also(::println)

    init {
        println("First initializer block that prints $name")
    }

    val secondProp = "Second Prop: ${name.length}".also(::println)

    init {
        println("Second initializer block that prints ${name.length}")
    }
}

//构造方法的参数作为类的属性并赋值,KotlinClass2在初始化时它的name与score属性会被赋值
class KotlinClass2(val name: String, var score: Int) { /*……*/ }

/**
 * 次构造方法
 */

class KotlinClass3(val view: View) {
    var views: MutableList<View> = mutableListOf()

    init {//初始化代码块会在次构造方法之前执行
        println("init")
    }

    constructor(view: View, index: Int) : this(view) {
        views.add(view)
        println("constructor")
    }
}

/**---------继承与覆盖---------**/
open class Animal(age: Int) {
    init {
        println(age)
    }

    open val foot: Int = 0
    open fun eat() {

    }
}

/**
 * 继承
 */
//派生类有主构造方法的情况
class Dog(age: Int) : Animal(age) {
    override val foot = 4
    override fun eat() {
    }
}

//派生类无柱构造方法的情况
class Cat : Animal {
    constructor(age: Int) : super(age)
}

/**---------属性的声明---------**/
class Shop {
    var name: String = "Android"
    var address: String? = null
    val isClose: Boolean
        get() = Calendar.getInstance().get(Calendar.HOUR_OF_DAY) > 11
    var score: Float = 0.0f
        get() = if (field < 0.2f) 0.2f else field * 1.5f
        set(value) {
            println(value)
        }
}

fun copyShop(shop: Shop): Shop {
    val shop = Shop()
    shop.name = shop.name
    shop.address = shop.address
    // ……
    return shop
}

/**---------延迟初始化属性与变量---------**/
class Test {
    lateinit var shop: Shop
    fun setup() {
        shop = Shop()
    }

    fun test() {
        if (::shop.isInitialized)
            println(shop.address)
    }
}

/**---------抽象类---------**/

abstract class Printer {
    abstract fun print()
}

class FilePrinter : Printer() {
    override fun print() {
    }
}

/**---------接口---------**/

interface Study {
    var time: Int// 抽象的
    fun discuss()
    fun earningCourses() {
        println("Android 架构师")
    }
}

//在主构造方法中覆盖接口的字段
class StudyAS(override var time: Int) : Study {
    //在类体中覆盖接口的字段
    //    override var time: Int = 0
    override fun discuss() {

    }
}

/**---------解决覆盖冲突---------**/

interface A {
    fun foo() {
        println("A")
    }
}

interface B {
    fun foo() {
        print("B")
    }
}

class D : A, B {
    override fun foo() {
        super<A>.foo()
        super<B>.foo()
    }
}

/**---------数据类---------**/

data class Address(val name: String, val number: Int) {
    var city: String = ""
    fun print() {
        println(city)
    }
}

fun testAddress() {
    val address = Address("Android", 1000)
    address.city = "Beijing"
    val (name, city) = address
    println("name:$name city:$city")
}


/**---------对象表达式---------**/
open class Address2(name: String) {
    open fun print() {

    }
}

class Shop2 {
    var address: Address2? = null
    fun addAddress(address: Address2) {
        this.address = address
    }

    // 私有方法,所以其返回类型是匿名对象类型
    private fun foo() = object {
        val x: String = "x"
    }

    // 公有方法,所以其返回类型是 Any
    fun publicFoo() = object {
        val x: String = "x"
    }

    fun bar() {
        val x1 = foo().x        // 没问题
//        val x2 = publicFoo().x  // 错误:未能解析的引用“x”
    }
}

fun test3() {
    //如果超类型有一个构造方法,则必须传递适当的构造方法参数给它
    Shop2().addAddress(object : Address2("Android") {
        override fun print() {
            super.print()
        }
    })
}

//只需要一个简单对象

fun foo() {
    val point = object {
        var x: Int = 0
        var y: Int = 0
    }
    print(point.x + point.y)
}

/**---------对象声明---------**/
object DataUtil {
    fun <T> isEmpty(list: ArrayList<T>?): Boolean {
        return list?.isEmpty() ?: false
    }
}

fun testDataUtil() {
    val list = arrayListOf("1")
    println(DataUtil.isEmpty(list))
}

/**---------伴生对象---------**/
class Student(val name: String) {
    companion object {
        val student = Student("Android")
        fun study() {
            println("Android 架构师")
        }
    }

    var age = 16
    fun printName() {
        println("My name is $name")
    }
}

fun testStudent() {
    println(Student.student)
    Student.study()
}

Kotlin中的泛型

泛型约束

在这里插入图片描述

  • 测试代码
fun main() {
//    Coke().price(Sweet())
//    BlueColor(Blue()).printColor()
    testSum()
}

/**---------泛型接口---------**/
interface Drinks<T> {
    fun taste(): T
    fun price(t: T)
}

open class Sweet {
    val price = 5
}

class Coke : Drinks<Sweet> {
    override fun taste(): Sweet {
        println("Sweet")
        return Sweet()
    }

    override fun price(t: Sweet) {
        println("Coke price:${t.price}")
    }
}

/**---------泛型类---------**/

abstract class Color<T>(var t: T/*泛型字段*/) {
    abstract fun printColor()
}

class Blue {
    val color = "blue"
}

class BlueColor(t: Blue) : Color<Blue>(t) {
    override fun printColor() {
        println("color:${t.color}")
    }

}

/**---------泛型方法---------**/
fun <T> fromJson(json: String, tClass: Class<T>): T? {
    /*获取T的实例*/
    val t: T? = tClass.newInstance()
    return t
}

/**---------泛型约束---------**/

fun <T : Comparable<T>?> sort(list: List<T>?){}

fun test12() {
    sort(listOf(1, 2, 3)) // OK,Int 是 Comparable<Int> 的子类型
//    sort(listOf(Blue())) // 错误:Blue 不是 Comparable<Blue> 的子类型
}

//多个上界的情况
fun <T> test(list: List<T>, threshold: T): List<T>
        where T : CharSequence,
              T : Comparable<T> {
    return list.filter { it > threshold }.map { it }
}

/**---------泛型中的out与in---------**/

fun sumOfList(list: List<out Number>): Number {
    var result = 0f
    for (v in list) {
        result += v.toFloat()
    }
    return result
}

fun testSum() {
    val result = sumOfList(listOf(1.5, 2, 3, 5.5))
    println("sumOfList:$result")
}

Kotlin中的协变与逆变

在Kotlin中out代表协变,in代表逆变,为了加深理解 我们可以将Kotlin的协变看成Java的上界通配符,逆变看成Java的下界通配符:
在这里插入图片描述

  • 总结
    Java泛型类型 | Java代码 | Kotlin代码 | Kotlin泛型类型|
    在这里插入图片描述
  • Kotlin泛型更加简洁安全,但是和Java一样都是有类型擦除的,都属于编译时泛型。

Kotlin注解

在这里插入图片描述

@Target

@Target顾名思义就是目标对象,也就是我们定义的注解能够应用于哪些目标对象,可以同时指定多个作用的目标对象。

  • @Target的原型
    在这里插入图片描述
  • AnnotationTarget枚举类源码
public enum class AnnotationTarget {
    /** Class, interface or object, annotation class is also included */
    CLASS,
    /** Annotation class only */
    ANNOTATION_CLASS,
    /** Generic type parameter (unsupported yet) */
    TYPE_PARAMETER,
    /** Property */
    PROPERTY,
    /** Field, including property's backing field */
    FIELD,
    /** Local variable */
    LOCAL_VARIABLE,
    /** Value parameter of a function or a constructor */
    VALUE_PARAMETER,
    /** Constructor only (primary or secondary) */
    CONSTRUCTOR,
    /** Function (constructors are not included) */
    FUNCTION,
    /** Property getter only */
    PROPERTY_GETTER,
    /** Property setter only */
    PROPERTY_SETTER,
    /** Type usage */
    TYPE,
    /** Any expression */
    EXPRESSION,
    /** File */
    FILE,
    /** Type alias */
    @SinceKotlin("1.1")
    TYPEALIAS
}

@Retention

在这里插入图片描述

  • AnnotationRetention源码
public enum class AnnotationRetention {
    /** Annotation isn't stored in binary output */
    SOURCE,
    /** Annotation is stored in binary output, but invisible for reflection */
    BINARY,
    /** Annotation is stored in binary output and visible for reflection (default retention) */
    RUNTIME
}
  • 要想在反射中可见,注解必须修饰成Runtime类型
  • 注解的使用场景
    在这里插入图片描述
  • 案例: 自定义注解实现API调用是的请求方法检查

fun main() {
    fire(ApiGetArticles())
}

//和一般的声明很类似,只是在class前面加上了annotation修饰符
@Target(AnnotationTarget.CLASS)
annotation class ApiDoc(val value: String)

/**
 * 自定义注解实现API调用时的请求方法检查
 */

@ApiDoc("修饰类")
class Box {
    //    @ApiDoc("修饰字段")
    val size = 6

    //    @ApiDoc("修饰方法")
    fun test() {

    }
}

public enum class Method {
    GET,
    POST
}

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class HttpMethod(val method: Method)

interface Api {
    val name: String
    val version: String
        get() = "1.0"
}

@HttpMethod(Method.POST)
class ApiGetArticles : Api {
    override val name: String
        get() = "/api.articles"
}

fun fire(api: Api) {
    val annotations = api.javaClass.annotations
    val method = annotations.find { it is HttpMethod } as? HttpMethod
    println("通过注解得知该接口需需要通过:${method?.method} 方式请求")

}

Kotlin扩展(Extensions)技术与应用

在这里插入图片描述

  • 扩展方法的原型
    在这里插入图片描述
  • 测试代码
fun main() {
    val test = mutableListOf(1, 2, 3)
    test.swap(1, 2)
    println(test)

}

fun MutableList<Int>.swap(index1: Int, index2: Int) {
    val tmp = this[index1]
    this[index1] = this[index2]
    this[index2] = tmp
}
  • 泛型扩展方法与扩展属性

fun main() {
    val test = mutableListOf(1, 2, 3)
    test.swap(1, 2)
    println(test)
    //在被扩展类的外部使用
    Jump().doubleJump(2f)
    Jump().test()

    val test2 = mutableListOf("Android Q", "Android N", "Android M")
    test2.swap1(0, 1)
    println(test2)

    val listString = listOf("Android Q", "Android N", "Android M")
    println("listString.last${listString.last}")

    Jump.print("伴生对象的扩展")

}

fun MutableList<Int>.swap(index1: Int, index2: Int) {
    val tmp = this[index1]
    this[index1] = this[index2]
    this[index2] = tmp
}


class Jump {
    companion object {}

    fun test() {
        println("jump test")
        //在被扩展类的内部使用
        doubleJump(1f)
    }
}

fun Jump.doubleJump(howLong: Float): Boolean {
    println("jump:$howLong")
    println("jump:$howLong")
    return true
}

//泛型化扩展函数
fun <T> MutableList<T>.swap1(index1: Int, index2: Int) {
    val tmp = this[index1]
    this[index1] = this[index2]
    this[index2] = tmp
}

/**---------扩展属性---------**/

//为String添加一个lastChar属性,用于获取字符串的最后一个字符
val String.lastChar: Char get() = this.get(this.length - 1)

///为List添加一个last属性用于获取列表的最后一个元素,this可以省略
val <T>List<T>.last: T get() = get(size - 1)

/**---------为伴生对象添加扩展---------**/
fun Jump.Companion.print(str: String) {
    println(str)
}

Kotlin中常用的扩展

  • let扩展
    在这里插入图片描述
  • 测试代码
/**
 * let
 */
fun testLet(str: String?) {
    //限制str2的作用域
    str.let {
        val str2 = "let扩展"
        println(it + str2)
    }
//    println(str2)//报错

    //避免为null的操作
    str?.let {
        println(it.length)
    }
}
  • run扩展
    在这里插入图片描述
  • apply扩展
    在这里插入图片描述
  • 测试代码
/**
 * apply
 */

fun testApply() {
    ArrayList<String>().apply {
        add("1")
        add("2")
        add("3")
        println("$this")
    }.let { println(it) }
}
  • 使用Kotlin扩展为控件绑定监听器减少模板代码
/**---------案例:使用Kotlin扩展为控件绑定监听器减少模板代码---------**/
//为Activity添加find扩展方法,用于通过资源id获取控件
fun <T : View> Activity.find(@IdRes id: Int): T {
    return findViewById(id)
}

//为Int添加onClick扩展方法,用于为资源id对应的控件添加onClick监听
fun Int.onClick(activity: Activity, click: () -> Unit) {
    activity.find<View>(this).apply {
        setOnClickListener {
            click()
        }
    }
}

Kotlin实用技巧

使用Kotlin安卓扩展,想findViewById说拜拜

  1. 在模块的 build.gradle文件中启用Gradle安卓扩展插件即可:
apply plugin: 'kotlin-android-extensions'

可以直接通过控件Id来访问控件实例

字符串的空判断,向TextUtils.isEmpty说拜拜

  • Kotlin中 CharSequence类的扩展方法isNullOrEmpty能帮我们省去对TextUtils.isEmpty的使用
    在这里插入图片描述
  • 除了isNullOrEmpty扩展之外,CharSequence还有个isNullOrBlank扩展,如果name都是空格,则TextUtils.isEmpty不满足使用。但isNullOrBlank仍可用。

使用@JvmOverloads告别繁琐的构造函数 重载

在Kotlin中@JvmOverloads注解的作用就是: 在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。这对我们自定义控件时特别有用。