一文了解 Kotlin 中的函数

380 阅读3分钟

不同于 Java,在 Kotlin 中,函数是一级公民。因此在 Kotlin 中的函数就拥有了很多不同于 Java 函数的高级特性。这篇文章就将介绍 kotlin 中函数的高级特性。

扩展函数

Kotlin 中,你可以从类的外部扩展出来的一个函数。代码示例如下:

// 定义一个判断字符串是否为 null 或者为空字符串的扩展函数
public fun String?.isNullOrEmpty(): Boolean {
    return this == null || this.isEmpty()
}

fun test(str: String?) {
    // 我们可以像 String 类的成员函数一样使用它
    if(str.isNullOrEmpty()) {
        ...
    }
}

扩展函数的实现也很简单,Kotlin 编译器会把 isNullOrEmpty 编译成 java 的静态方法,入参为 String。它等同于如下的Java代码:

public static boolean isNullOrEmpty (String str) {  
    return str == null || str.length() <= 0;  
}

但是扩展函数也有两点不足:

  1. Kotlin 扩展不是真正的类成员,因此它无法被它的子类重写
  2. 除此之外,扩展函数只能操作接收者的 Public 的属性

高级函数

Kotlin 中,函数可以视为一种类型。因此我们可以把函数作为参数,也可以作为返回值。像这种将函数用作参数或返回值的函数,我们把它叫做高阶函数。代码示例如下:

class Member(var name: String, var id: String) {

    // 函数作为参数
    // (String) -> Unit 其中 String 表示入参类型,Unit 表示返回值类型
    fun methode(block: (String) -> Unit) {
        block("")
    }
    
    // 函数作为返回值
    fun methode1(): (String) -> Unit {
        return ::method2
    }
    
    // 如果要表示挂起函数,需要加上 suspend
    fun methode(block: suspend (String) -> Unit) {
        CoroutineScope(Dispatchers.IO).launch {
            block("")
        }
    }
}

为什么 Kotlin 需要引入高阶函数呢?答案是为了简化代码。考虑一下,如果没有高阶函数,我们就需要创建对应的接口来实现方法的回调。对应的 Java 代码如下所示:

interface Callback {
    void call(String str);
}

void method(Callback callback) {
    callback.call("");
}

带接收者的函数类型

kotlin 中还有一种特殊的函数类型,叫做带接收者的函数类型。初看名词可能有点懵,我们直接看代码:

// String 作为入参的函数类型
fun method(block: (String) -> Unit) {

}

// String.() -> Unit 就是带接收者的函数类型, String.() 表示入参带有 String;
// 因此 method1 其实等同于 method
fun method1(block: String.() -> Unit) {  
  
}

fun test() {
    // 不同点是 method 需要用 it 指定入参的 String
    method() {
       println(it) 
    }
    // 而在 method1 中用 this 指定入参的 String
    method1() {
       println(this) 
    }  
}

可以看到 带接收者的函数类型 就是帮我们做了一下简化,因为访问入参的属性时,this 是可以省略的,而 it 不可以。除此之外,示例中的 method1 完全等同于 method

构造函数

上面介绍完了 Kotlin 函数的高级特性,这里再补充一下 Kotlin 的构造函数。在 kotlin 中,构造函数声明如下所示:

//主构造函数
class Person(val name: String, val age: Int) {
    
    //次构造函数
    constructor(name: String, age: Int, id: String): this(name, age) {  
  
    }
    
}

在Kotlin中,在有默认参数值的方法中使用 @JvmOverloads 注解,就可以很方便地实现多个重载方法。最常使用的地方就是自定义 View,代码示例如下:

class MyView @JvmOverloads constructor(context:Context, 
                                       attributeSet: AttributeSet? = null, 
                                       defStyleAttr: Int = 0): View(context, attributeSet, defStyleAttr) {
}

它等价于如下 java 代码:

public class MyView extends View {

    public MyView(Context context) {
        this(context, null);
    }

    public MyView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}