一起来学Kotlin:概念:7. Kotlin 函数介绍:扩展函数,中缀函数,运算符函数,带有varrag输入的函数,内联函数
这里介绍Kotlin的不同种类函数,包括:一般的函数表达方式,扩展函数,中缀函数,运算符函数,带有varrag
输入的函数,内联函数。
文章目录
1 一般函数
直接上一些例子,都挺一目了然的。
fun printMessage(message: String): Unit {
println(message)
}
// 一个简单的函数,它接受一个 String 类型的参数并返回 Unit (即没有返回值)。
fun printMessageWithPrefix(message: String, prefix: String = "Info") {
println("[$prefix] $message")
}
// 这个函数第二个参数带默认值,其返回类型被省略,实际上和上面这个函数一样,返回 Unit。
fun sum(x: Int, y: Int): Int {
return x + y
}
// 返回整数的函数。
fun multiply(x: Int, y: Int) = x * y
// 返回整数(推断)的单表达式函数。(single-expression function)
fun main() {
printMessage("Hello") // 5
printMessageWithPrefix("Hello", "Log") // 6
printMessageWithPrefix("Hello") // 7
printMessageWithPrefix(prefix = "Log", message = "Hello") // 8
println(sum(1, 2)) // 9
println(multiply(2, 4)) // 10
}
2 扩展函数
我们以如何对一个字符串计数为例,java的方式可以这么写:
object StringUtil{
fun letterCount(str:String):Int{
var count=0
for(char in str){
if(char.isLetter()){
count++
}
}
return count
}
}
fun main() {
val str="ABC123xyz!@#"
val count=StringUtil.letterCount(str)
}
对于Kotlin,我们可以使用一种更加面向对象的思维实现-将方法添加到现有类中(扩展函数)。
fun String.letterCount(): Int {
var count = 0
for (char in this) {
if (char.isLetter())
count++
}
return count
}
val count1="ABC123xyz!@#".letterCount()
3 中缀函数 (infix function)
中缀函数为为成员函数或者扩展函数,并且只有一个参数、参数不能是可变参数且不能有默认值, 并且使用infix修饰。
对于Kotlin的map
的使用就涉及到中缀函数:
fun main(args: Array<String>) {
val map = mapOf(1 to "one", 2 to "two")
map.map(::println)
}
//result:
//1=one
//2=two
这val map = mapOf(1 to "one", 2 to "two")
里的to
就是一个infix function。其源码实现:
public infix fun <A, B> A.to(that: B):
Pair<A, B> = Pair(this, that)
接下来还罗列了一些中缀函数的例子:
fun main() {
infix fun Int.times(str: String) = str.repeat(this)
// 基于 Int 定义了一个中缀函数
println(2 times "Bye ")
// 返回 Bye Bye
val pair = "Ferrari" to "Katrina"
println(pair)
// 上面的例子可以看到,to 本来就是一个中缀函数,实现的就是 Pair 的功能
infix fun String.onto(other: String) = Pair(this, other)
val myPair = "McLaren" onto "Lucas"
println(myPair)
// 这是我们自定义的 to 的调用的实现
val sophia = Person("Sophia")
val claudia = Person("Claudia")
sophia likes claudia
// 中缀表示法也适用于成员函数(方法)
}
class Person(val name: String) {
val likedPeople = mutableListOf<Person>()
infix fun likes(other: Person) { likedPeople.add(other) }
}
4 运算符重载函数 (Operator Functions)
Kotlin允许我们将所有的运算符,关键字进行重载,扩展他们的用法。
operator fun Int.times(str: String) = str.repeat(this)
println(2 * "Bye ")
// times() 的运算符符号是 *,因此您可以使用 2 * "Bye" 调用该函数。
operator fun String.get(range: IntRange) = substring(range)
// 运算符函数允许对字符串进行轻松的范围访问。
val str = "Always forgive your enemies; nothing annoys them so much."
println(str[0..14])
// get() 运算符启用括号访问语法。
class Money(val value: Int) {
operator fun plus(money: Money): Money {
val sum = value + money.value
return Money(sum)
}
}
fun main() {
val money1=Money(5)
val money2=Money(10)
val money3=money1+money2
println(money3.value)
// 15
}
更复杂的,运算符多重重载:
class Money(val value: Int) {
operator fun plus(money: Money): Money {
val sum = value + money.value
return Money(sum)
}
operator fun plus(newValue: Int): Money {
val sum = value + newValue
return Money(sum)
}
}
fun main() {
val money1 = Money(5)
val money2 = Money(10)
val money3 = money1 + money2
val money4 = money3 + 20
println(money3.value) //15
println(money4.value) //35
}
这里附上语法糖表达式和实际调用函数对照表:
语法糖表达式 | 实际调用函数 |
---|---|
a + b | a.plus(b) |
a - b | a.minus(b) |
a * b | a.times(b) |
a / b | a.div(b) |
a % b | a.rem(b) |
a ++ | a.inc() |
a - | a.dec() |
+a | a.unaryPlus() |
-a | a.unaryMinus() |
!a | a.not() |
a == b | a.equals(b) |
a >= b | a.compareTo(b) |
a … b | a.rangeTo(b) |
a[b] | a.get(b) |
a[b] = c | a.set(b,c) |
a in b | b.contains(a) |
5 vararg
Varargs
允许我们通过用逗号分隔参数来传递任意数量的参数。
fun printAll(vararg messages: String) {
for (m in messages) println(m)
}
// 可变参数修饰符将参数转换为可变参数。
printAll("Hello", "Hallo", "Salut", "Hola", "你好")
// 允许使用任意数量的字符串参数调用 printAll。
fun printAllWithPrefix(vararg messages: String, prefix: String) {
for (m in messages) println(prefix + m)
}
printAllWithPrefix(
"Hello", "Hallo", "Salut", "Hola", "你好",
prefix = "Greeting: "
)
// 我们也可以在可变参数之后添加另一个相同类型的参数。
fun log(vararg entries: String) {
printAll(*entries)
}
// 在运行时,可变参数只是一个数组。 要将其传递到可变参数参数中,请使用特殊的扩展运算符 *,它允许我们传入 *entries(String 的 vararg)而不是 Array<String>。
6 内联函数
当一个函数被内联 inline 标注后,在调用它的地方,会把这个函数方法体中的所有代码移动到调用的地方。比如如下代码:
// 在 main() 中调用 makeTest()
fun main() {
Log.i("zc_test", "main() start")
makeTest()
Log.i("zc_test", "main() end")
}
// 内联函数 makeTest()
private inline fun makeTest() {
Log.i("zc_test", "makeTest")
}
使用 inline 编译成 java 的代码
public final void main() {
Log.i("zc_test", "main() start");
int $i$f$makeTest = false;
Log.i("zc_test", "makeTest");
Log.i("zc_test", "main() end");
}
当 makeTest() 不在被 inline 修饰时, 被编辑成 java 的代码为:
public final void main() {
Log.i("zc_test", "main() start");
this.makeTest();
Log.i("zc_test", "main() end");
}
可以看到,当 makeTest() 被inline 修饰时, 在 main() 中原来调用 makeTest() 的地方被替换成了 makeTest() 里面的代码。
什么时候应该使用内联函数?
每次使用集合操作时,我们都会使用内联函数,比如说 map,filter 等。如此一来可以更好地实现干净和快速的函数式编程。
val newList = listOf(1, 2, 3, 4).map { it * 2 }
// compiles to
val temp: MutableList<Int> = mutableListOf()
for (x in listOf(1, 2, 3, 4)) {
temp.add(x * 2)
}
val newList = temp.toList()
inline 能带来的性能提升,往往是在参数是 lambda 的函数上。