• kotlin基础教程:<3>函数的高级用法和字符串的基础操作


    大家都知道函数的功能强大,在Java,有构造函数,静态函数,自定义的函数,自带的函数,反正很多函数,上一期我们讲解了一下Java以及kotlin函数的一个格式,kotlin的函数是由作用域,fun关键字,函数名,参数列表,返回值和函数体组成的,是有明显的逻辑的输入输出函数;那么这一期,我们来讲一讲函数的一些高级用法,比如函数的函数(lambda作为参数),返回值为lambda的函数,相信大家对lambda 表达式不陌生了:

    1. //1.2 一般用在含有lambda参数的函数中
    2. inline fun methods1(name: String,study: String,showresult: (String)->Unit){
    3. val str = "显示结果:${name} ${study}"
    4. showresult(str)
    5. }

    也就是这个函数的第三个参数,就是我们上一期讲到的匿名函数,使用:或者等号创建的那个函数,叫做匿名函数,实质上就是lambda表达式,这里出现了一个inline关键字,叫做内联引用,他一般用在含有lambda表达式的函数中,目的就是为了减少性能的损耗,因为如果不使用内联的话,程序在运行的时候会调用很多和对象,涉及到一些对象的开辟,降低了运行的性能,但是如果使用了内联的话,kotlin会将我们写的函数的主要逻辑代码全部放在main函数中执行,相当于是一个宏替换,就不会需要多次调用和开辟对象了;

    那么这一期我们就讲一下函数的一些高级用法和字符串的一些基础操作,顺便将异常给讲了:

    1、函数的函数

    就是将一个函数作为另一个函数的参数,在Java中我们需要定义接口才可以实现

    1. //2.1 函数的函数:就是以一个函数作为参数
    2. /*
    3. * 用户名和密码
    4. * */
    5. const val username_db = "Tony"
    6. const val pwd_db = "123456"
    7. //2.2 定义一个检测用户名和密码的函数
    8. private fun logincheck(username: String,pwd: String,sponseresult:(String,Int)-> Unit){
    9. //2.3 如果长度不对就报错
    10. if(username == null || pwd == null){
    11. TODO("请输入用户名或密码!")
    12. }
    13. //2.4 合格就验证
    14. if(username.count() > 3 && pwd.count()>3){
    15. if(login(username,pwd)){
    16. sponseresult("登陆成功",200)
    17. }else{
    18. sponseresult("登陆失败",444)
    19. }
    20. }
    21. }
    22. //2.5 检验是否是否正确
    23. private fun login(username: String,pwd: String): Boolean{
    24. return if(username == username_db && pwd == pwd_db) true else false
    25. }

    还是这个登录的例子,我们只实现了一个简单的登录验证的功能,如果用户名和密码长度大于3且都正确的话就输出登陆成功,否则失败;

    这里的第三个参数是一个lambda表达式,只是一个函数的规则而已,但是后面我们调用它的时候就需要按照这个规则来引用函数。

    1. //调用登录检测函数,其实这后面的大括号就是lambda表达式参数而已,只是这样写方便;
    2. logincheck("Tony2","123456"){
    3. msg:String,code:Int->
    4. println("最终登录结果:${msg} ${code}")
    5. }

    可以看到,这里调用的时候我们使用的是第三种传参方式,以后大多数需要我们在调用的时候写执行体都是这种传参方式,大家不要觉得很奇怪,其实见多了就好了,匿名函数其实参数是写在大括号里面的,在定义普通的函数的函数时只是定义了一个规则,到时候传过来的函数参数就必须要是满足这个规则,当然,我们完全可以使用普通函数来传参,不用匿名函数:

    1. fun login2(msg:String,code:Int){
    2. println("最终登录结果:${msg} ${code}")
    3. }
    4. //3.1 如果我们想要引用别的基本函数来当做函数的lambda参数,我们使用::来引用
    5. /*效果是一样的*/
    6. logincheck("Tony","123456",::login2)

    其实效果还是一样的,注意我们的函数参数是一个函数对象,所以我们在引用普通函数的时候需要使用::来将其转化;

    2、返回值为函数的函数:

    我们只需要定义一个普通的函数,在函数的的返回值部分我们写一个函数类型,也就是小括号加箭头和返回值的形式,也可以叫做是函数的规则,然后返回的话就是通过大括号的方式,也就是返回一个匿名函数;

    1. //3.2 我们可以使用返回值为一个函数的函数
    2. fun showmethods(info: String): (String,Int) ->String{
    3. return {name: String,age: Int -> "你好 ${info} 你的用户名: ${name} 你的年龄: ${age}"}
    4. }
    5. //这个函数的返回值是一个函数对象
    6. val result2 = showmethods("Tony")
    7. //当然是可以进行调用的:
    8. println(result2("jerry",18))

    不难想到,既然是返回值为函数,不就是在我们申明函数的时候将返回值改为函数规则就可以了?

    然后返回的话可就是一个函数对象,通过大括号括起来的;

    3、函数的引用

    其实我们上期就讲过了,不就是匿名和具名调用吗?其实在我们传函数参数的时候也是如此:

    1. fun main() {
    2. //1.1 函数的匿名和具名
    3. //匿名调用
    4. methods1("Tony","studing kt lanugage"){
    5. println("${it}")
    6. }
    7. //1.3 具名调用,结果都是一样的,只不过我们使用一个普通函数来传递
    8. methods1("Tony","studing kt lanugage",::showresult)
    9. }
    10. //1.2 一般用在含有lambda参数的函数中
    11. inline fun methods1(name: String,study: String,showresult: (String)->Unit){
    12. val str = "显示结果:${name} ${study}"
    13. showresult(str)
    14. }
    15. //1.4 具名调用需要用到一个普通函数
    16. fun showresult(msg:String){
    17. println(msg)
    18. }

    注意我们一般申明带有lambda参数的函数是要写上inline,放在fun前面就可以;如果不写作用域的话,默认就是public;

    4、接下来讲安全问题,我们最常见的问题就是空指针异常,其实在kotlin当中,含有一套机制可以有效避免空指针异常,就是在我们给一个变量传入空值的时候程序会报一个对象不能为空值的错误,如果我们想要它为空吗,就需要声明其类型为可空类型:

    1. //2.1 关于kotlin的安全机制
    2. var name: String ? = "李四"
    3. name = null
    4. //?可用于表示可空类型,还可以用来判断拦截当变量为空时后面执行的函数
    5. name?.capitalize()
    6. val r = name
    7. println(r)

    就只需要我们在类型的后面添加一个?就可以了,这里的capitalize()方法是一个将字符串的首字母大写的函数;

    另外还有一种带问号的安全机制,也是判断是否为空,如果为空执行问号后面的函数,算是一种检测类型的函数:

    1. //2.5 我们可以在外面定义一个函数来抛出异常
    2. fun checkException(info: String?){
    3. //进行空值判断,如果为null我们执行抛异常,用到了我们的合并操作符
    4. info ?: throw CustomException()
    5. }

    就好比这个抛出异常的函数里面的,如果info为null的话,程序就会抛出一个异常,符号为?:

    既然讲到了异常,那么就放我们来看看什么是异常吧:

    5、异常处理机制:

    说到异常,我们就可以联想到try catch,如果想要深刻的理解它,我们最好还是自定义一个异常:

    1. //2.5 我们可以在外面定义一个函数来抛出异常
    2. fun checkException(info: String?){
    3. //进行空值判断,如果为null我们执行抛异常,用到了我们的合并操作符
    4. info ?: throw CustomException()
    5. }
    6. //2.6 我们可以自己定义一个异常类
    7. class CustomException: IllegalArgumentException("代码不够严谨")
    8. //2.4 异常怎么用
    9. try{
    10. var name1 :String ? = null
    11. //这里我们想要抛出一个数值为null的异常
    12. checkException(name1)
    13. //断言非空
    14. println(name1!!.length)
    15. }catch (e: Exception){
    16. println("捕获异常了:${e}")
    17. }

    首先,我们要理解我们需要解决的异常,在catch里面就会捕获到,然后自定义一个捕获异常的类,在我们的捕获异常函数里面会将这个异常信息抛出,捕获异常函数是我们自己写的,抛出的异常也是自己写的;至于这里的断言,我告诉大家,就是在你们写代码很自信的时候就可以用断言,无非就是!!然后调用函数就可以了,作用就是确保了一个对象不为null;

    注意这里的2.4是放在main函数里面执行的,大家不要看不懂;

    另外:

    1. //3.1 其实kt有自带抛异常的方法
    2. var name2 : String? = null
    3. var name3 : Boolean = false
    4. checkNotNull(name2)
    5. requireNotNull(name2)
    6. require(name3)

    它们可以检测对象是否为空,最后一个检测是否为false;

    6、再就是我们的字符串:

    1、字符串的截取,我们使用substring:

    1.  //3.2 截取字符串操作
    2.    var name4 = "Hello Tony"
    3.    var index = name4.indexOf('T')
    4.    println(name4.substring(0 until index).length)

    2、字符串的替换,我们使用replace,使用正则表达式和传入lambda执行替换的操作:

    1. //1.1 字符串的替换操作,可以模拟加解密操作
    2. val Pwd = "abc123456"
    3. //加密:放一个正则表达式和lambda参数
    4. val secretPwd = Pwd.replace(Regex("[a36]")){
    5. //必须要返回想要替换的字符
    6. when(it.value){
    7. "a" -> "9"
    8. "3" -> "y"
    9. "6" -> "x"
    10. else -> it.value
    11. }
    12. }
    13. println("加密之后的密码:${secretPwd}")
    14. //解密操作,需要反规则
    15. val sourcePwd = secretPwd.replace(Regex("[9yx]")){
    16. //必须要返回想要替换的字符
    17. when(it.value){
    18. "9" -> "a"
    19. "y" -> "3"
    20. "x" -> "6"
    21. else -> it.value
    22. }
    23. }
    24. println("解密之后的密码:${sourcePwd}")

    3、字符串的比较,在kotlin里面有两种,(==)和(===)前者是内容的比较,和Java中的equals作用一致,后者是引用比较,也就是在常量池中是否用的是同一个对象:

    1.    //1.2 字符串的比较有两种,一种是内容比较(==),一种是引用比较(===)
    2.    val name1 = "Tony"
    3.    var name2 = "tony"
    4.    println(name1 == name2)
    5.    println(name1 === name2)
    6.    //如果我们将name2转换成name1的内容会怎么样呢?
    7.    name2 = name2.capitalize()
    8.    println("转换后的:")
    9.    println(name1 == name2)
    10.    println(name1 === name2)

    这里两个字符串内容是不一致的,那么在常量池中肯定就不是同一个对象,所以引用比较和内容比较都是false,而当我们将name2的内容转化为name1内容一致的内容时,内容比较将会一致,但是引用比较是不一致的,因为它是由name2的初始值转换过来的,也就是说,引用对象在我们初始化一个对象的时候就已经创建好了,后面的引用比较都是对初始对象的比较;

    4、字符串的遍历,我们可以使用foreach来遍历字符串,有多少个字符就循环多少次:

    1.    //1.3 字符串的遍历
    2.    val name3 = "qwerttyu"
    3.    //这里的it代表对象和函数的共同作用的结果
    4.    println("遍历的结果:")
    5.    name3.forEach {
    6.        print(it)
    7.   }

    不过需要注意我们这里的it,表示的是对象和函数的共同作用结果,在这里就是一个一个的字符

    5、字符串的转化

    如何将字符串安全的转化为int类型呢?

    1. //1.4 字符串转int
    2.    //这种转化方式更加安全,可以为空如果转化失败的话就是空
    3.    val number1 : Int ? = "666".toIntOrNull()
    4.    println(number1)
    5.    //这种转化方式比较低级,如果我们字符串格式不对,是double类型的话就会报错
    6.    val number2 : Int  = "666".toInt()
    7.    println(number2)

    一般我们使用上面一种好,以为它不会导致程序崩溃;

     总之函数的传参一般是对象,所以我们一定要清楚自已写的函数到底是需要什么,就传什么,那么这一期就到这,咱们下期见。

  • 相关阅读:
    macOS下matplotlib如何显示中文字体?
    Rocky Linux 配置邮件发送
    为什么吃奶粉的宝宝比吃母乳宝宝更胖?
    (二十一)大数据实战——Flume数据采集之复制和多路复用案例实战
    JS课程记录-黑马
    手写RPC框架-项目结构以及使用
    环境变量基础
    任务七 特征工程 LDA
    Spring Data Redis + RabbitMQ - 基于 string + hash 实现缓存,计数(高内聚)
    操作系统:系统调用
  • 原文地址:https://blog.csdn.net/aiwanchengxu/article/details/125416697