Kotlin语言(九):特性

1、尾递归优化

  1. 尾递归:函数在调用自己之后没有再执行其他任何操作就是尾递归

  2. 尾递归优化的原理就是将递归转换成迭代,避免栈溢出

  3. 将递归转成尾递归步骤:

    • 将递归修改成尾递归
    • 加上 tailrec 关键字
  4. 迭代和递归对比

优点 缺点
迭代 内存开销小 需抽象出数学模型
递归 逻辑简单易实现 内存开销大,容易栈溢出
/**
 * 1、用迭代的方式求 1 到 n 的和
 */
fun sum0(n: Int): Int {
    var result = 0
    var temp = n
    while (temp > 0) {
        result += temp
        temp--
    }
    return result
}

/**
 * 2、用递归方式求 1 到 n 的和
 */
fun sum1(n: Int): Int {
    return if (n == 1) {
        1
    } else {
        n + sum1(n - 1)
    }
}

/**
 * 3、尾递归优化后求 1 到 n 的和
 */
tailrec fun sum2(n: Int, result: Int = 0): Int {
    return if (n == 1) {
        result + 1
    } else {
        sum2(n - 1, result + n)
    }
}

fun main() {
    println(sum0(10)) //55
    println(sum1(10)) //55

    println(sum0(100000)) //705082704
    //println(sum1(100000)) //java.lang.StackOverflowError

    //尾递归优化后调用
    println(sum2(100000)) //705082704
}

2、运算符重载

  1. kotlin 中每个运算符都对应于一个方法

  2. 运算符重载即重写对应的方法,步骤如下:

    • 找到运算符对应的函数
    • 在函数前面加上 operator 关键字
    • 不关心参数和返回值
  3. 运算符对应的方法,可以通过如下两个方式查找:

    • 官网(kotlinlang.org)-> LEARN -> Reference -> Other -> Operator overloading
    • 在开发工具里面按住 control/command 左键点击运算符会跳转到其对应的方法去
class Dog {
    var name = "包子"
    var age = 10

    operator fun plus(dog: Dog): Dog {
        this.age += dog.age
        return this
    }

    operator fun plus(age: Int): Dog {
        this.age += age
        return this
    }

    operator fun plus(name: String): Int {
        this.name = name
        return this.age
    }

    override fun toString(): String {
        return "[name=$name, age=$age]"
    }
}

fun main() {
    val dog1 = Dog()
    val dog2 = Dog()
    println(dog1.toString()) //[name=包子, age=10]
    println(dog2.toString()) //[name=包子, age=10]

    val dog3 = dog1 + dog2
    println(dog3.toString()) //[name=包子, age=20]

    val dog4 = dog1 + 30
    println(dog4.toString()) //[name=包子, age=50]

    val dog5 = dog1 + "饺子"
    println(dog5) //50
}

3、中缀表达式

  1. 方法前面加上 infix 关键字
  2. 必须是成员函数或者扩展函数
  3. 函数必须只有一个参数
  4. 参数不能是可变参数或者默认参数
class Person(var name: String) {
    infix fun sayHelloTo(name: String) {
        println("${this.name} say hello to $name")
    }
}

fun main() {
    val p = Person("ZhangSan")
    p.sayHelloTo("LiSi")  // ZhangSan say hello to LiSi
    p sayHelloTo "LiSi"   // ZhangSan say hello to LiSi

    val pair = 12 to 20 // Pair(10, 20)
    println(pair)

    val range = 1..5
    println(range)
    val range2 = 1 until 5
    println(range2)
}

4、委托模式(by)

  1. 委托模式通过关键字 by 来实现
  2. 委托模式分为:类委托 和 属性委托
interface WashPower {
    fun wash()
}

class BigHeadSon : WashPower {
    override fun wash() {
        println("大头儿子 -> 开始洗碗了")
    }
}

class SmallHeadSon : WashPower {
    override fun wash() {
        println("小头儿子 -> 开始洗碗了")
    }
}

/**
 * 1、委托实现1:只能委托给大头儿子
 */
class SmallHeadFather : WashPower by BigHeadSon()

/**
 * 2、委托实现2:委托给有洗碗能力的人
 */
class SmallHeadFather2(washPower: WashPower) : WashPower by washPower

/**
 * 3、委托加强:在委托前后添加自己的功能
 */
class SmallHeadFather3(private val washPower: WashPower) : WashPower by washPower {
    override fun wash() {
        println("支付1块洗碗的钱")
        washPower.wash()
        println("很棒,洗的真干净")
    }
}

/**
 * 4、属性委托:将属性的 get 和 set 委托给其他对象
 * 这里手动实例化了一个 Wife 对象,也可以通过构造函数传递过来
 */
class Husband {
    var salary: Int by Wife()
}

/*
class Husband2(wife: Wife) {
    var salary: Int by wife
}
*/

class Wife {
    var husbandSalary = 0
    var selfSalary = 0

    // 丈夫取工资
    operator fun getValue(husband: Husband, property: KProperty<*>): Int {
        return husbandSalary
    }

    // 丈夫存工资:小于 50 直接全部没收;大于 50 存 50,剩余的没收
    operator fun setValue(husband: Husband, property: KProperty<*>, i: Int) {
        husbandSalary += if (i > 50) 50 else 0
        selfSalary += if (i > 50) i - 50 else i
    }

}

fun main() {
    val smallHeadFather = SmallHeadFather()
    smallHeadFather.wash()
    // 大头儿子开始洗碗了

    val bigHeadSon = BigHeadSon()
    val smallHeadSon = SmallHeadSon()
    val smallHeadFather2_0 = SmallHeadFather2(bigHeadSon)
    smallHeadFather2_0.wash()
    // 大头儿子开始洗碗了
    val smallHeadFather2_1 = SmallHeadFather2(smallHeadSon)
    smallHeadFather2_1.wash()
    // 小头儿子开始洗碗了

    val smallHeadFather3 = SmallHeadFather3(bigHeadSon)
    smallHeadFather3.wash()
    // 支付1块洗碗的钱
    // 大头儿子 -> 开始洗碗了
    // 很棒,洗的真干净

    val husband = Husband()
    husband.salary = 100;
    println(husband.salary)
    // 50
}

5、延迟 lazy 和 lateinit

by lazy lateinit
1、必须是 val 不可变的 1、必须是 var 可变的
2、知道具体的值,用到的时候才加载 2、不知道具体的值,用到的时候再赋值
3、可以单独存在,也可以是成员变量 3、可以单独存在,也可以是成员变量
4、返回值是最后一行
5、是线程安全的
val lazyProp: String by lazy { "张三" }

class LazyLoad {
    val lazyProp: String by lazy {
        println("惰性加载初始化")
        "李四"
    }
}


lateinit var lateInitProp: String

class LateInitLoad {
    lateinit var lateInitProp: String
}


fun main() {
    println(lazyProp)
    // 张三

    val lazyLoad = LazyLoad()
    println(lazyLoad.lazyProp)
    // 惰性加载初始化
    // 李四
    println(lazyLoad.lazyProp)
    // 李四


    // println(lateInitProp)
    // 未赋值不能使用,如果使用会抛出 kotlin.UninitializedPropertyAccessException
    lateInitProp = "单独的 lateinit 属性"
    println(lateInitProp)
    // 单独的 lateinit 属性

    val lateInitLoad = LateInitLoad()
    lateInitLoad.lateInitProp = "类成员 lateinit 属性"
    println(lateInitLoad.lateInitProp)
    // 类成员 lateinit 属性
}

6、扩展函数

  1. 不改变已有类的情况下,为类添加新的函数
  2. 主要用来替代 java 里面的 util 类
  3. 父类的扩展函数,子类可以调用,但是不能重写
fun String?.myIsEmpty(): Boolean {
    return this == null || this.length == 0
}


open class Father
class Son : Father()

fun Father.sayHello() {
    println("Father's sayHello")
}


fun main() {
    var str: String? = null
    println(str.myIsEmpty())
    // true

    str = "kotlin"
    println(str.myIsEmpty())
    // false

    val father = Father()
    father.sayHello()
    // Father's sayHello
    val son = Son()
    son.sayHello()
    // Father's sayHello
}

7、Object 单例

  1. 所有字段都是 private static 的
  2. 所有的方法都是 public final 的,非 static 的
  3. 默认会创建一个当前类 INSTANCE 对象,并通过静态代码块初始化
  4. 字段和方法都是通过 INSTANCE 对象来访问
  5. 适用于字段不多的情况下
object Single {
    var prop = "Single"
    fun sayHello() {
        println("Single hello")
    }
}

fun main() {
    //val single = Single()  // 不能创建对象
    println(Single.prop)     // Single
    Single.sayHello()        // Single hello
}

8、伴生对象

伴生对象 companion object:

  1. companion object 内部的成员通过类名调用,外部的成员通过实例对象调用
  2. companion object 外部的都是普通的成员变量和方法
  3. companion object 内部的变量是 private static 的,方法是 public final 的
  4. companion object 会生成一个 public static final class Companion 内部类,并且构造函数是私有的
  5. 外部类会初始化一个 public static final CompanionObject.Companion Companion 对象
  6. 外部类通过 Companion 对象去调用 companion object 内的成员
class CompanionObject {

    var prop = "Normal prop"
    fun hello() {
        println("Normal hello")
    }

    companion object {
        var staticProp = "Static prop"
        fun staticHello() {
            println("Static hello")
        }
    }
}

fun main() {
    val companionObject = CompanionObject()
    println(companionObject.prop) // Normal prop
    companionObject.hello()       // Normal hello

    println(CompanionObject.staticProp) // Static prop
    CompanionObject.staticHello()       // Static hello
}

9、kotlin 懒汉单例

  1. 私有化构造函数
  2. 使用伴生对象定义私有静态类对象
  3. 使用 by lazy 来懒加载对象
class LazySingle private constructor() {

    val prop = "kotlin 懒汉单例属性"
    fun sayHello() {
        println("hello kotlin 懒汉单例方法")
    }

    companion object {
        // 1、懒加载;2、只初始化一次;3、线程安全
        val INSTANCE by lazy { LazySingle() }
    }
}

fun main() {
    println(LazySingle.INSTANCE.prop)
    // kotlin 懒汉单例属性
    LazySingle.INSTANCE.sayHello()
    // hello kotlin 懒汉单例方法
}
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

相关阅读更多精彩内容

友情链接更多精彩内容