Kotlin | 3.函数的定义与调用

2021-02-22 11:12:36 浏览数 (1)

本章内容包括:

  • 用于处理集合、字符串和正则表达式的函数
  • 使用命名参数、默认参数,以及中辍调用的语法
  • 通过扩展函数和属性来适配Java库
  • 使用顶层函数、局部函数和属性架构代码

1、在Kotlin中创建集合

代码语言:javascript复制
// 支持数字创建
        val set = hashSetOf(1, 7, 53)

        // 用类似的方法创建一个 list 或 map:
        val list = arrayListOf(1, 7, 53)
        val map = hashMapOf(1 to "one", 7 to "seven, 53 to fifty-three")
        /**注意: to 并不是一个特殊的结构,而是一个普通函数。后面讨论。*/

        // javaClass 相当于 java 中的getClass()
        LogUtil.e(set.javaClass.toString())
        LogUtil.e(list.javaClass.toString())
        LogUtil.e(map.javaClass.toString())
        /**
         * class java.util.HashSet
         * class java.util.ArrayList
         * class java.util.HashMap
         * Kotlin 没有采用它自己的集合类,而是采用的标准的Java集合类。
         */

        // 获取最后一个元素
        val string = listOf("first", "second", "fourteenth")
        LogUtil.e(string.last())

        // 取最大值
        val numbers = setOf(1, 14, 2)
        LogUtil.e(numbers.max().toString())


        // 2.让函数更好的调用  测试
        val list2 = listOf(1, 2, 3)
        LogUtil.e(joinToString(list2, ";", "(", ")"))

2、让函数更好的调用

代码语言:javascript复制
/**
     * val list = listOf(1,2,3)
     * println(list)    --- 触发了 toString()的调用
     * 默认输出  [1,2,3]
     * 想要效果  (1;2;3)
     *
     * joinToString() 的基本实现
     * 通过在元素中间添加分割符号,从直接重写实现函数开始,然后再过渡到Kotlin更惯用的方法来重写。
     */
    /**
     * @param collection 集合
     * @param separator 分割符
     * @param prefix 前缀
     * @param postfix 后缀
     * 有默认值的参数
     */
    fun <T> joinToString(collection: Collection<T>,
                         separator: String = ",",
                         prefix: String = "",
                         postfix: String = ""
    ): String {

        val result = StringBuilder(prefix)

        for ((index, element) in collection.withIndex()) {
            // 不用在第一个元素前添加分隔符
            if (index > 0) {
                result.append(separator)
            }
            result.append(element)
        }
        result.append(postfix)
        return result.toString()
    }
代码语言:javascript复制
 /*---------------2.1、命名参数---------------*/
        joinToString(collection = list2, separator = "", prefix = "", postfix = ".")

        /*---------------2.2、默认参数值  解决 [重载] 重复问题---------------*/
//        fun <T> joinToString(
//                collection: Collection<T>,
//                separator: String = ",",
//                prefix: String = "",
//                postfix: String = ""
//        ): String {
//
//        }


        LogUtil.e(joinToString(list2))
        LogUtil.e(joinToString(list2, ";"))
        LogUtil.e(joinToString(list2, ",", ",", ","))
        LogUtil.e(joinToString(list2, postfix = ",", prefix = "#"))

        /*---------------2.3、消除静态工具类:顶层函数和属性---------------*/
        /*
         *  顶层函数:
         *  声明joinToString()作为顶层函数
         *  新建 strings 包,里面直接放置 joinToStrings
         */
        joinToStrings(list2, ",", ",", ",")
代码语言:javascript复制
package com.kotlin.jingbin.kotlinapp.function.strings

import com.kotlin.jingbin.kotlinapp.utils.LogUtil

/**
 * Created by jingbin on 2019/1/28.
 * 3.2.3、消除静态工具类:顶层函数和属性
 */
fun <T> joinToStrings(collection: Collection<T>,
                      separator: String = ",",
                      prefix: String = "",
                      postfix: String = ""): String {

    val result = StringBuilder(prefix)

    for ((index, element) in collection.withIndex()) {
        // 不用在第一个元素前添加分隔符
        if (index > 0) {
            result.append(separator)
        }
        result.append(element)
    }
    result.append(postfix)
    return result.toString()
}


// 声明一个 顶层属性
var opCount = 0

// 改变属性的值
fun performOperation() {
    // 改变属性的值
    opCount  
    // ...
}

// 读取属性的值
fun reportOperationCount() {
    LogUtil.e("Operation performed $opCount times")
}

// 如果是val就只有一个getter,如果是var就对应一对getter和setter

const val UNIX_LINE_SEPATOR = "n"
// 等同于 Java
//public static final String UNIX_LINE_SEPATOR = "n";


/*
* String: 接受者类型
* this: 接受者对象
* */
fun String.lastChar(): Char = this.get(this.length - 1)

3.给别人的类添加方法:扩展函数和属性

join.kt:

代码语言:javascript复制
/**
 * 扩展函数:
 * String: 接受者类型
 * this: 接受者对象
 * */
fun String.lastChar(): Char = this.get(this.length - 1)

fun String.lastChar2(): Char = get(length - 1)

/**
 * 扩展属性:
 * */
val String.lastChar: Char
    get() = get(length - 1)

/**
 * 可变的扩展属性:
 */
var StringBuilder.lastChar2: Char
    // getter 属性
    get() = get(length - 1)
    // setter 属性
    set(value: Char) {
        this.setCharAt(length - 1, value)
    }
代码语言:javascript复制
        LogUtil.e("Kotlin".lastChar())

        /*---------------3.1、导入和扩展函数---------------*/
        // import com.kotlin.jingbin.kotlinapp.function.strings.lastChar
        val lastChar = "Kotlin".lastChar()
        // 可以用关键字as 来修改导入的类或者函数名称:  [可以用来解决命名冲突]
        // import com.kotlin.jingbin.kotlinapp.function.strings.lastChar as last
        val last = "Kotlin".last()


        /*---------------3.2、从Java中调用扩展函数---------------*/
        // java    -->   StringUtil.lastChar("Kotlin");
        // java    -->   lastChar("Kotlin");
        // kotlin  -->   "Kotlin".lastChar()


        /*---------------3.3、作为扩展函数的工具函数---------------*/
        // joinToString 函数的终极版本,和kotlin标准库中看到的一模一样
        // 为Collection<T> 声明一个扩展函数
        fun <T> Collection<T>.joinToString(separator: String = ",", prefix: String = "", postfix: String = ""): String {
            val result = StringBuilder(prefix)

            for ((index, element) in this.withIndex()) {
                // 不用在第一个元素前添加分隔符
                if (index > 0) {
                    result.append(separator)
                }
                result.append(element)
            }
            result.append(postfix)
            return result.toString()
        }
        LogUtil.e(list2.joinToString(";", "((", "))"))

        // 扩展函数无非是静态函数的一个高效语法糖
        fun Collection<String>.join(separator: String = ",", prefix: String = "", postfix: String = "") = joinToString(separator, prefix, postfix)

        LogUtil.e(listOf("one", "two", "eight").join(" "))


        /*---------------3.4、不可重写的扩展函数---------------*/
        // 在Kotlin中,重写成员函数是很平常的一件事情。但是,不能重写扩展函数。

        /**这是扩展函数的写法!扩展了 View.class 的函数方法*/
        fun View.showOff() = LogUtil.e("I'm a view")

        /**
         * 这是扩展函数的写法!扩展了 Button.class 的函数方法。
         * 而 Button 继承于 View ,但是输出为 View的扩展函数的内容,因为 “扩展函数并不存在重写,因为Kotlin会把它们当做作静态函数对待”
         * 如果 Button 直接重写 View 类里面的 showOff() ,则是生效的,因为不是扩展函数,写法不一样!!
         * */
        fun Button.showOff() = LogUtil.e("I'm a Button")

        val view: View = Button(this)
        LogUtil.e(view.showOff()) //  I'm a view
        // 扩展函数并不存在重写,因为Kotlin会把它们当做作静态函数对待


        /*---------------3.5、扩展属性  join.kt  ---------------*/
        // val kotlin.String.lastChar: Char
        //     get() = get(length - 1)
        "Kotlin".lastChar
        val builder = StringBuilder("Kotlin?")
        builder.lastChar2 = '!'
        LogUtil.e(builder) // Kotlin!

4.处理集合: 可变参数、中辍调用和库的支持

这一节将会展示 Kotlin 标准库中用来处理集合的一些方法。另外也会涉及几个相关的语法特性:

  • 可变参数的关键字 vararg ,可以用来声明一个函数将可能有任意数量的参数。
  • 一个中辍表示法,当你在调用一些只有一个参数的函数时,使用它会让代码更简练
  • 解构声明,用来把一个单独的组合值展开到多个变量中
代码语言:javascript复制
        /*---------------4.1、扩展 Java集合的API  ---------------*/

        // 基于 Kotlin中的集合与Java的类相同,但是对API做了扩展。
        val strings = listOf("first", "second", "fourteenth")
        LogUtil.e(strings.last())
        val of = setOf(1, 15, 3)
        val numbers2: Collection<Int> = setOf(1, 14, 2)
        println(of.max())       //15
        println(numbers2.max()) //14


        /*---------------4.2、可变参数: 让函数支持任意数量的参数  ---------------*/

        // 当你创建一个函数列表的时候,可以传任意个人的参数给它
        val listOf = listOf(2, 3, 4, 5, 6)
        // 如果看看这个函数在库中的声明:
        fun <T> listOf(vararg elements: T): List<T> = if (elements.size > 0) elements.asList() else emptyList()

        /**
         * 可变参数与Java类似
         * 不同点:
         *  - java   使用的是 三个点
         *  - kotlin 使用的是 vararg
         *
         *  另一个区别:当需要传递的参数已经包装在数组中时,调用该函数的语法。
         *  技术来讲,这个功能称为  展开运算符,使用的时候不过是在对应的参数前面放一个 *
         */
        fun main(args: Array<String>) {
            // 展开运算符展开数组内容
            val list = kotlin.collections.listOf("args:", *args)
            println(list)
        }


        /*---------------4.3、键值对的处理:中辍调用和解构声明  ---------------*/

        // 可以使用 mapOf 函数来创建map:
        val mapOf = mapOf(1.to("one"), 2 to "two", 7 to "seven")
        /**
         *  单词 to 不是内置的结构,而是一种特殊的函数调用,被称为 中辍调用。
         *  中辍调用中,没有添加额外的分隔符,函数名称是直接放在目标对象名称和参数之间的。
         *  等价:
         *   - 1.to("one")   // 一般 to 函数的调用
         *   - *  2 to "two" // 使用中辍符号调用的 to 函数
         */

        // 如果使用中辍符号,需要使用 infix 修饰符类标记它 (Any 超类)
        infix fun Any.to(ohther: Any) = Pair(this, ohther)


        /**
         * 解构声明:
         * 用 to 函数创建一个pair,然后用解构声明来展开
         */
        val (number, name) = 1 to "one"
        LogUtil.e(number)
        LogUtil.e(name)


        // 解构声明不止运用于 pair 也适用于循环
        // 打印 val strings = listOf("first", "second", "fourteenth")
        for ((index, element) in strings.withIndex()) {
            LogUtil.e("$index: $element")
        }

5.字符串和正则表达式的处理

代码语言:javascript复制
        /*---------------5.1 分割字符串  ---------------*/
        val split = "12.345-6.A".split(".")
        // 指定多个分隔符
        val split2 = "12.345-6.A".split(".", "-")

        // 12 345-6  A
        LogUtil.e(split)
        // 12  345  6   A
        LogUtil.e(split2)
        // java处理
        SetOfJava().start()


        /*---------------5.2 正则表达式和三重引号的字符串---------------*/

        // 使用String的扩展函数来解析文件路径
        fun parsePath(path: String) {
            val directory = path.substringBeforeLast("/")
            val fullName = path.substringAfterLast("/")
            val fileName = fullName.substringBeforeLast(".")
            val extension = fullName.substringBeforeLast(".")
            LogUtil.e("Dir: $directory, name: $fileName, ext: $extension")
        }
        parsePath("/Users/yole/kotlin-book/chapter.adoc")

        // 使用正则表达式解析文件路径
        fun parsePath2(path: String) {
            val toRegex = """(. )/(. ).(. )""".toRegex()
            val matchResult = toRegex.matchEntire(path)
            if (matchResult != null) {
                val (directory, fileName, extension) = matchResult.destructured
                LogUtil.e("Dir2: $directory, name2: $fileName, ext2: $extension")
            }
        }
        parsePath2("/Users/yole/kotlin-book/chapter.adoc")

        """  (. )        /     (. )       .         (. )"""
        """  目录   最后一个斜线  文件名    最后一个点     扩展名"""


        /*---------------5.3 多行三重引号的字符串---------------*/
        val kotlinLogo = """|//
            .|//
            .|/"""
        // trimMargin 来删除每行中的前缀和前面的空格
        LogUtil.e(kotlinLogo.trimMargin("."))
        /*
        |//
        |//
        |/
        */

        // 不用转义字符 
        """C://Usersyolekotlin-book"""
        // 使用美元?字符
        """${'$'}99.9"""

6.让你的代码更整洁:局部函数和扩展

代码语言:javascript复制
        // 带重复代码的函数
        class User(val id: Int, val name2: String, val address: String)

        fun saveUser(user: User) {
            if (user.name2.isEmpty()) {
                throw IllegalArgumentException("Can't save user ${user.name2}: empty Name")
            }

            if (user.address.isEmpty()) {
                throw IllegalArgumentException("Can't save user ${user.address}: empty Address")
            }
            // 保存user到数据库
        }

        // 提取局部函数避免重复 -->  在局部函数中访问外层函数的参数 --> 提取逻辑到扩展函数
        fun saveUser2(user: User) {
            fun validate(user: User, value: String, field: String) {
                if (value.isEmpty()) {
                    // 可以直接访问外部函数的参数
                    throw IllegalArgumentException("Can't save user ${user.id}: empty $field")
                }
            }

            // 在局部函数中访问外层函数的参数
            // 不需要在 saveUser2 函数中重复 user 参数
            fun validate2(value: String, field: String) {
                if (value.isEmpty()) {
                    throw IllegalArgumentException("Can't save user ${user.id}: empty $field")
                }
            }
            validate(user, user.name2, "Name")
            validate2(user.name2, "Name")
        }

        // 提取逻辑到扩展函数
        fun User.validateBeforeSave() {
            fun validate3(value: String, fieldName: String) {
                if (value.isEmpty()) {
                    // 可以直接访问 user 的属性
                    throw IllegalArgumentException("Can't save user $id: empty $fieldName")
                }
            }
            validate3(name2, "Name")
            validate3(address, "Address")
        }

        fun saveUser3(user: User) {
            // 扩展函数
            user.validateBeforeSave()
            // 保存user到数据库
        }

        // java.lang.IllegalArgumentException: Can't save user 2: empty Name
        saveUser3(User(2, "haha", "china"))

总结

  • Kotlin 没有定义自己的集合类,而是在Java集合类的基础上提供了更丰富的API。
  • Kotlin 可以给函数参数定义默认值,这样大大降低了重载函数的必要性,而且命名参数让多参数函数的调用更加易读。
  • Kotlin 允许更灵活的代码结构:函数和属性都可以直接在文件中声明,而不仅仅在类中作为成员。
  • Kotlin 可以用扩展函数和属性来扩展任何类的API,包括在外部中定义的类,而不需要修改其源代码,也没有运行时的开销。
  • 中辍调用提供了处理单个参数的,类似调用运算符方法的简明语法。
  • Koltin 为普通字符串和正则表达式都提供了大量的方便字符串处理的函数。
  • 三重引号的字符串提供了一种简洁的方式,解决了原本在Java中需要进行大量啰嗦的转义和字符串连接的问题。
  • 局部函数帮助你保持代码的整洁的同时,避免重复。

0 人点赞