扔物线Kotlin讲解学习(二)----Kotlin函数的特性

一.函数简化:使用 = 连接返回值

我们已经知道了 Kotlin 中函数的写法:

fun area(width: Int, height: Int): Int {
    return width * height
}

其实,这种只有一行代码的函数,还可以这么写:

fun area(width: Int, height: Int): Int = width * height

{} 和 return 没有了,使用 = 符号连接返回值。

我们之前讲过,Kotlin 有「类型推断」的特性,那么这里函数的返回类型还可以隐藏掉:

fun area(width: Int, height: Int) = width * height

不过,在实际开发中,还是推荐显式地将返回类型写出来,增加代码可读性。

以上是函数有返回值时的情况,对于没有返回值的情况,可以理解为返回值是 Unit:

fun sayHi(name: String) {
    println("Hi " + name)
}

简化完函数体,我们再来看看前面的参数部分。

对于 Java 中的方法重载,我们都不陌生,那 Kolin 中是否有更方便的重载方式呢?接下来我们看看 Kotlin 中的「参数默认值」的用法。

二. 参数默认值

Java 中,允许在一个类中定义多个名称相同的方法,但是参数的类型或个数必须不同,这就是方法的重载:

public void sayHi(String name) {
    System.out.println("Hi " + name);
}

public void sayHi() {
    sayHi("world"); 
}

在 Kotlin 中,也可以使用这样的方式进行函数的重载,不过还有一种更简单的方式,那就是「参数默认值」:

fun sayHi(name: String = "world") = println("Hi " + name)

这里的 world 是参数 name 的默认值,当调用该函数时不传参数,就会使用该默认值。

这就等价于上面 Java 写的重载方法,当调用 sayHi 函数时,参数是可选的:

sayHi("kaixue.io")
sayHi() // 使用了默认值 "world"

既然与重载函数的效果相同,那 Kotlin 中的参数默认值有什么好处呢?仅仅只是少写了一些代码吗?

其实在 Java 中,每个重载方法的内部实现可以各不相同,这就无法保证重载方法内部设计上的一致性,而 Kotlin 的参数默认值就解决了这个问题。

不过参数默认值在调用时也不是完全可以放飞自我的。

来看下面这段代码,这里函数中有默认值的参数在无默认值参数的前面:

fun sayHi(name: String = "world", age: Int) {
    ...
}

sayHi(10)
//    👆 这时想使用默认值进行调用,IDE 会报以下两个错误
// The integer literal does not conform to the expected type String
// No value passed for parameter 'age'

这个错误就是告诉你参数不匹配,说明我们的「打开方式」不对,其实 Kotlin 里是通过「命名参数」来解决这个问题的。

三.命名参数

具体用法如下:

fun sayHi(name: String = "world", age: Int) {
    ...
}
      👇   
sayHi(age = 21)

在调用函数时,显式地指定了参数 age 的名称,这就是「命名参数」。Kotlin 中的每一个函数参数都可以作为命名参数。

再来看一个有非常多参数的函数的例子:

fun sayHi(name: String = "world", age: Int, isStudent: Boolean = true, isFat: Boolean = true, isTall: Boolean = true) {
    ...
}

当函数中有非常多的参数时,调用该函数就会写成这样:

sayHi("world", 21, false, true, false)

当看到后面一长串的布尔值时,我们很难分清楚每个参数的用处,可读性很差。通过命名参数,我们就可以这么写:

sayHi(name = "wo", age = 21, isStudent = false, isFat = true, isTall = false)

与命名参数相对的一个概念被称为「位置参数」,也就是按位置顺序进行参数填写。

当一个函数被调用时,如果混用位置参数与命名参数,那么所有的位置参数都应该放在第一个命名参数之前:

fun sayHi(name: String = "world", age: Int) {
    ...
}

sayHi(name = "wo", 21) // 👈 IDE 会报错,Mixing named and positioned arguments is not allowed
sayHi("wo", age = 21) // 👈 这是正确的写法

讲完了命名参数,我们再看看 Kotlin 中的另一种常见函数:嵌套函数。

四.本地函数(嵌套函数)

首先来看下这段代码,这是一个简单的登录的函数:

fun login(user: String, password: String, illegalStr: String) {
    // 验证 user 是否为空
    if (user.isEmpty()) {
        throw IllegalArgumentException(illegalStr)
    }
    // 验证 password 是否为空
    if (password.isEmpty()) {
        throw IllegalArgumentException(illegalStr)
    }
}

该函数中,检查参数这个部分有些冗余,我们又不想将这段逻辑作为一个单独的函数对外暴露。这时可以使用嵌套函数,在 login 函数内部声明一个函数:

fun login(user: String, password: String, illegalStr: String) {
           👇 
    fun validate(value: String, illegalStr: String) {
      if (value.isEmpty()) {
          throw IllegalArgumentException(illegalStr)
      }
    }
   👇
    validate(user, illegalStr)
    validate(password, illegalStr)
}

这里我们将共同的验证逻辑放进了嵌套函数 validate 中,并且 login 函数之外的其他地方无法访问这个嵌套函数。

这里的 illegalStr 是通过参数的方式传进嵌套函数中的,其实完全没有这个必要,因为嵌套函数中可以访问在它外部的所有变量或常量,例如类中的属性、当前函数中的参数与变量等。

我们稍加改进:


fun login(user: String, password: String, illegalStr: String) {
    fun validate(value: String) {
        if (value.isEmpty()) {
                                              👇
            throw IllegalArgumentException(illegalStr)
        }
    }
    ...
}

这里省去了嵌套函数中的 illegalStr 参数,在该嵌套函数内直接使用外层函数 login 的参数 illegalStr。

上面 login 函数中的验证逻辑,其实还有另一种更简单的方式:

fun login(user: String, password: String, illegalStr: String) {
    require(user.isNotEmpty()) { illegalStr }
    require(password.isNotEmpty()) { illegalStr }
}

其中用到了 lambda 表达式以及 Kotlin 内置的 require 函数,这里先不做展开,之后的文章会介绍。

版权声明
本文首发于:https://rengwuxian.com/kotlin-basic-3/
微信公众号:扔物线
转载时请保留此声明

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值