函数参数之惑
当一个函数拥有多个参数,且存在多个相同类型参数紧挨着的情况时,往往不太清楚传入参数的位置是否正确,且严重影响函数的可读性。需要调用者跳转到函数对应的地方,对参数和函数定义中的参数列表进行匹对。这将对函数调用者造成很大的麻烦和困扰。
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()
}
针对这种情况,或许可以依靠IDE进行优化。如:Idea早已对此进行了优化,在对函数填写参数时,会将参数对应位置的参数名称进行提示。
命名参数
Kotlin在语法层上对该情况进行优化,当调用一个Kotlin定义的函数时,可以显示标明参数的名称。这种参数叫命名参数。
当指明一个参数的名称后,避免混淆,其他参数都要标明名称。(既然显式的标明名称,也就不需要按原本参数定义的顺序传入参数)
joinToString(array,prefix = "(",separator = ",", postfix = "]")
注意:
- 既然显示的标明了参数名,也就意味着当参数名或方法名进行改变时,其显式标明的参数名或方法名也需要进行改变。这时可以使用Idea的Rename进行修改。(先选中方法名或参数名 -> Refactor -> Rename)
- 当调用Java定义的函数时,不能采用命名参数,因为Java8之前不能把参数名存在,class文件中。而Kotlin需要与Java6兼容。所以,编译器不能识别出函数参数的名称。
函数重载之祸
在Java中,支持对函数进行重载。这就造成多个相同名称的函数,且其参数间只有细微的差别。当调用省略部分参数的函数时,可能不清楚到底调用的是哪一个函数。(例如:Thread类拥有8个构造函数)
默认参数
而Kotlin只需要指定参数的默认值,就可以有效避免创建多个重载函数。这种带有默认值的函数参数叫做默认参数。再配合命名参数进行使用时,可以很方便的对指定参数进行赋值,从而实现重载。
fun <T> joinToString(collection: Collection<T>,
separator:String = ",",
prefix:String = "",
postfix:String = ""):String
调用时只需要传入具体的集合对象,函数会使用默认参数的默认值对其进行运算。
当然,按参数定义的顺序,传入对应的参数也完全没有问题。
val string = joinToString(array)
//像以前传递前缀,分割符和后缀也没有问题
val string = joinToString(array,"(",",")
val string = joinToString(array,"(",",", "]")
//配合命名参数食用,效果更佳
val string = joinToString(array,separator = ";")
@JvmOverloads 提高Kotlin与Java的交互性
Java 中没有默认参数的概念,当从Java中调用Kotlin的函数时,必须显示地传递所有参数值。为了让Java调用者能调用该方法的重载函数,可以用@JvmOverloads注解它。在编译时,编译器会从最后一个参数开始逐个省略,生成Java的重载函数。
局部函数
程序猿都认为方法越小越好,相比纵向冗长的代码片段,将其按照职责切分成功能单一的小的局部方法,最后组织起来调用是最好的。但很多时候分解出来的小方法之间并没有什么明确的关系,最后以一个包含许多小方法的类告终。
Kotlin中支持局部函数,所谓局部函数就是在方法中声明方法,内部方法可以获取到外部函数的参数和局部变量。可以将各个小方法定义为局部方法,即提供所需的简洁结构也无须额外的语法开销。
data class Person(val age:String?,val name:String?)
fun daqi(person: Person){
if (person.age == null){
throw IllegalArgumentException()
}
if (person.name == null){
throw IllegalArgumentException()
}
//正常操作
}
转换为局部函数:
fun daqi(person: Person){
//需要在顶层定义,不然函数未定义无法使用
fun personFileIsEmpty(value:String?,fileName:String){
if (value == null){
throw IllegalArgumentException("$fileName is null")
}
}
personFileIsEmpty(person.age,"age")
personFileIsEmpty(person.name,"name")
//正常操作
}
局部函数的缺点是:局部函数的不能声明为内联,并且拥有局部函数的的函数也不能声明为内联。暂时没有任何办法避免这种函数的调用成本。
参考资料:
- 《Kotlin实战》
- Kotlin官网