Normal 和 Single Expression 函数定义之间的差异

Differences between Normal and Single Expression function definitions

假设一个 Android 项目,其中我有两个按钮 XML:

<layout>
    <data>
        <variable name="viewModel" type="com.package.package.UploadPhotoViewModel" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center_horizontal"
        android:orientation="vertical"
        android:padding="10dp">

        <com.zoosk.zoosk.ui.widgets.ProgressButton
            android:id="@+id/progressButtonChooseFromLibrary"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{viewModel.choosePhotoText}"
            android:onClick="@{viewModel::choosePhotoButtonClick}"
            android:visibility="@{viewModel.choosePhotoVisibility}" />

        <com.zoosk.zoosk.ui.widgets.ProgressButton
            android:id="@+id/progressButtonTakePhoto"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{viewModel.takePhotoText}"
            android:onClick="@{viewModel::takePhotoButtonClick}"
            android:visibility="@{viewModel.takePhotoVisibility}" />

    </LinearLayout>
</layout>

以及随附的 ViewModel:

class UploadPhotoViewModel(resources: Resources) {
    var onChoosePhotoButtonClicked: ((View) -> Unit)? = null
    var onTakePhotoButtonClicked: ((View) -> Unit)? = null

    fun choosePhotoButtonClick(v: View) {
        onChoosePhotoButtonClicked?.invoke(v)
    }
    fun takePhotoButtonClick(v: View) = onTakePhotoButtonClicked?.invoke(v)
}

特别注意 choosePhotoButtonClicktakePhotoButtonClick 声明方式的区别。

构建此项目时,Android 的数据绑定对于 choosePhotoButtonClick 将正常工作,但对于 takePhotoButtonClick 会抛出错误,表示没有与预期的参考签名匹配的方法.假设这些方法在后台以相同的方式创建,这不应该发生,唉,肯定有一些区别。

这两种声明语法到底有什么区别? official Kotlin documentation 没有提到任何功能,只是它可以作为声明该方法的附加方式。

如果你在没有指定 return 类型的情况下使用花括号,你的函数正在隐式地重新调整 Unit(这是 Kotlin for void),所以你的方法:

fun choosePhotoButtonClick(v: View) {
    onChoosePhotoButtonClicked?.invoke(v)
}

签名为:

fun choosePhotoButtonClick(v: View) : Unit

(IDE 会提示 return 类型 "Unit" 是多余的,可以省略)。

然而,对 shorthand 使用等号的函数会在右侧推断出 return 类型的表达式,例如:

fun itemCount() = items.size

会推断return类型的Int,所以它的签名是:

fun itemCount() : Int

你的情况是:

fun takePhotoButtonClick(v: View) = onTakePhotoButtonClicked?.invoke(v)

函数可以是 return nullonTakePhotoButtonClicked 为 null 或 return 该方法的值 (Unit),这意味着你的 takePhotoButtonClick 签名是:

fun takePhotoButtonClick(v: View) : Unit?

(OP)直接解决问题:

fun takePhotoButtonClick(v: View) = onTakePhotoButtonClicked?.invoke(v) ?: Unit

可以认为比大括号版本可读性差,但这意味着 return 类型的 Unit 而不是 Unit?并将通过数据绑定正确拾取。