无法理解如何在 Kotlin 中使用 SAM 接口

Can't understand how to use SAM interface in Kotlin

我不明白为什么下面的代码有效: 山姆接口:

    fun interface WebResponseHandler
{
    fun onWebResponseFinished(jsonObject:JSONObject)
}

并且 Implemnetaiton 看起来像:

  val onWebResponseHandler: VolleyHandler.WebResponseHandler = VolleyHandler.WebResponseHandler()
{
    fun onWebResponseFinished(jsonObject: JSONObject) {
        Log.v("da", "dsa")
    }

    fun foo() {
        it.getString("name")
    }
}

foo 函数如何知道 it 是什么?毕竟,foo 函数不是 VolleyHandler.WebResponseHandler 接口的一部分,仅当接口仅作为一个 method.Function Foo 在没有任何参数的情况下被调用时才有效,那么为什么 it 的含义在那里?

此外,如果我在尝试实现接口时从接口声明中省略关键字 fun,则会出现另一个问题:

val onWebResponseHandler: VolleyHandler.WebResponseHandler = VolleyHandler.WebResponseHandler()
{
    fun onWebResponseFinished(jsonObject: JSONObject) {
        Log.v("da", "dsa")
    }

    fun foo() {
        Log.v("something", "something")

    }
}

我收到一个编译错误

Interface WebResponseHandler does not have constructors

这两种情况有什么不同?如果我删除 () 我会收到很多不同的错误,例如:

Function declaration must have a name

我希望有人能弄清楚这里有什么问题

不是 Kotlin 很乱,而是你使用了错误的语法来做你想做的事情。

fun interface让你做的就是写

val onWebResponseHandler = VolleyHandler.WebResponseHandler {
    jsonObject: JSONObject -> Log.v("da", "dsa")
}

你可以用普通界面做的是写

val onWebResponseHandler = object : VolleyHandler.WebResponseHandler() {
    fun onWebResponseFinished(jsonObject: JSONObject) {
        Log.v("da", "dsa")
    }

    fun foo() {
        Log.v("something", "something")

    }
}

您的代码执行了 none 的这些操作,并且在编译时不会执行您想要的任何操作。你写的相当于

val onWebResponseHandler = VolleyHandler.WebResponseHandler() { 
  unusedJsonObject: JSONObject ->
    fun aFunctionWhichIsDefinedAndNeverCalled(jsonObject: JSONObject) {
        Log.v("da", "dsa")
    }

    fun anotherFunctionWhichIsDefinedAndNeverCalled() {
        unusedJsonObject.getString("name")
    }
    // doesn't actually do anything at all when executed
    // just defines functions which aren't ever invoked
}

...编译是因为,因为它是一个 fun interface,你 可以 只是将一个 lambda 传递给构造函数,但实际上并没有做任何类似的事情你期待。

这里有一种不同的解释方式,可能会有帮助。

如果要将接口的实现分配给变量或 属性,通常必须使用像这样的匿名 class 语法。注意 object: override.

的使用
val onWebResponseHandler = object : VolleyHandler.WebResponseHandler {
    override fun onWebResponseFinished(jsonObject: JSONObject) {
        Log.v("da", jsonObject.toString())
    }
}

您还可以在匿名 class 中定义其他 public 函数,但这毫无意义,因为无法访问匿名 class 的唯一成员,因此您在此示例中将无法调用函数 foo()

val onWebResponseHandler = object : VolleyHandler.WebResponseHandler {
    override fun onWebResponseFinished(jsonObject: JSONObject) {
        Log.v("da", jsonObject.toString())
    }

    fun foo() {
        Log.v("something", "something")
    }
}

如果您将接口设为 fun interface,则可以使用 lambda 语法来定义这样的单个函数。这就是 SAM 转换。它将您的 lambda 视为接口的匿名实现。

val onWebResponseHandler = VolleyHandler.WebResponseHandler { jsonObject ->
    Log.v("da", jsonObject.toString())
}

另外,如果一个lambda只有一个参数,你可以省略定义参数名,而简单地命名为it 所以这等同于上面的:

val onWebResponseHandler = VolleyHandler.WebResponseHandler {
    Log.v("da", it.toString())
}

另外请注意,您可以在代码中的任何位置定义一个函数,包括在另一个函数的中间。例如:

fun bar() {
    
    fun foo() {
        Log.i("foo", "foo")
    }

    foo()
}

相当于

fun bar() {
    
    val foo: () -> Unit = {
        Log.i("foo", "foo")
    }

    foo() // or foo.invoke()
}

因此,在您的原始代码中,您结合了这些概念来创建一个 lambda 函数,其中定义了其他从未使用过的函数。混淆可能是因为它看起来类似于我的代码块,其中我们使用 object: 语法定义匿名 class。最大的区别是您省略了 object:override,因此您使用的是 lambda 语法。您的代码相当于:

val onWebResponseHandler = VolleyHandler.WebResponseHandler { jsonObject ->

    val onWebResponseFinished: (JSONObject) -> Unit = {
        Log.v("da", "dsa")
    }

    val foo: () -> Unit = {
        jsonObject.getString("name")
    }

}

如果没有 lambda 语法(SAM 转换),它看起来像

val onWebResponseHandler = object : VolleyHandler.WebResponseHandler {
    override fun onWebResponseFinished(jsonObject: JSONObject) {
        val onWebResponseFinished: (JSONObject) -> Unit = {
            Log.v("da", "dsa")
        }

        val foo: () -> Unit = {
            jsonObject.getString("name")
        }
    }
}