SAM 和匿名函数的区别

Difference between SAM and anonymous functions

我最近在 Java 中偶然发现了 SAM(单一抽象方法)的概念。

我在 Scala 中编程,对我来说,它们看起来与匿名函数(或 lambda)相同,但是我的 IntelliJ 建议我在不同的场景中使用 SAM 或 lambda。

示例 1:

val x = new Function1[Int, Int] { // here usage of lambda is suggested
  override def apply(x: Int): Int = x * 2
}

此处建议使用 lambda。

示例 2:

val event: EventHandler[ActionEvent] = new EventHandler[ActionEvent]() { // here usage of SAM is suggested
  override def handle(e: ActionEvent): Unit = {
    println("hi")
  }
}

我很难看出匿名函数和 SAM 之间的语法差异。两者在概念上或功能上有什么区别吗?

FunctionN 类型在 scala 中是特殊的,因为匿名函数被自动推断为该类型。 val twice = (x: Int) => x * 2 被推断为 Function1[Int, Int],而不是任何其他类型,除非预期类型是(不同的)SAM 接口。

如果预期类型是 SAM 接口——具有单个抽象方法的接口——它将转换为该类型。

语法几乎相同,区别在于上下文。

函数是语言支持的接口,它允许将 2 个函数组合成一个函数,由集合等支持。

Single Abstract Method 是任何 trait 或 class,它只有一个方法来实现,因此采用此方法的签名,将其转换为函数的签名并让编译器将此类函数重写为匿名 class 实例。

明显差异:

  • 编译器只能将函数重写到 SAM 中,前提是您提供的类型明显表明您不想在 SAM 中使用函数
  • SAM 不会扩展函数接口,因此您将无法执行诸如sam1 andThen sam2 除非您自己以某种方式在其中添加这些方法
  • 您不能只获取某些东西返回的函数并将其用作 SAM - 函数语法必须用于初始化 SAM 对象,在创建它的地方

我想您可以将它们视为同一个概念 - 一些 trait/class 仅具有一个使用函数语法初始化的抽象方法 - 其中一些 FunctionX 接口将是“默认”并且编译器会插入它,而其他一些实现将需要显式类型归属并称为 SAM。

无论我们将 SAM 视为函数语法的重用,还是将函数视为 SAM 的特例,都不会改变 SAM 不是 lambdas/closures/anonymous 函数的事实。此名称仅用于打算用作函数的事物,即函数接口的实现(Function1Function2、...、PartialFunction 等)。