GEP-12


元数据
编号

GEP-12

标题

SAM 强制转换

版本

4

类型

功能

状态

最终

负责人

Jochen "blackdrag" Theodorou

创建日期

2013-05-30

最后修改时间 

2018-10-29

摘要: SAM 强制转换

SAM 代表单一抽象方法。SAM 类型是一个只有一个抽象方法的抽象类或接口。SAM 强制转换涉及将一个groovy.lang.Closure实例转换为适合我们 SAM 类型的对象。强制转换可以在赋值时发生,也可以在方法调用时发生。由于这种转换可能超出了 Closure 本身提供的类型,因此它可能不仅仅是简单的 Java 风格的强制转换。Closure 成为所有 SAM 类型的子类型。Groovy 有其他此类转换,不需要显式强制转换或 asType 使用,例如数字对象转换以及 GString 到 String 的转换。

动机

早在 Java8 之前,我们就讨论过如何使用 Closure 支持不同的接口,例如 Runnable 和 Callable。这两个都是简单的例子,任何框架都可以定义无数的接口和抽象类。然后,需要“Groovify”库,编写一个能够将 Closure 对象转换为库理解的格式的辅助层。虽然这种方法不太可能使 Groovy Builder 变得多余,但它仍然可以帮助简化集成。

“单一抽象方法”的含义

对于根据此 GEP 成为 SAM 类型的 SAM 类型,需要一个只有一个抽象方法的抽象类或接口。任何静态方法以及非抽象方法都不算在内。抽象方法可以在 SAM 类本身中定义,也可以在父类中定义。但是,该方法所需的可见性修饰符是 public。

SAM 示例

  • 简单接口

interface SAM {
  def foo()
}
  • 带有 defender 方法(也称为虚拟扩展方法)的接口

interface SAM {
  def foo()
  def bar() default { 1 }
}
  • 从另一个接口继承的接口

interface ParentOfSAM {}
interface SAM extends ParentOfSAM {
  def foo()
}
  • 从另一个接口继承的接口,但本身没有定义方法

interface ParentOfSAM {
  def foo()
}
interface SAM extends ParentOfSAM {}
  • 简单抽象类

abstract class SAM {
  abstract foo()
}
  • 带有抽象方法和非抽象方法的抽象类

abstract class SAM {
  abstract foo()
  def bar() { 1 }
}
  • 扩展其他类的抽象类

abstract class ParentOfSAM1 {
  abstract foo()
}
  • abstract class SAM1 extends ParentOfSAM1 {}

class ParentOfSAM {
   def bar() { 1 }
}
abstract class SAM2 extends  {
  abstract foo()
}
  • 实现接口的抽象类

interface SomeInterface{
  def foo()
}
abstract class SAM1 implements SomeInterface {}
abstract class SAM2 implements Runnable{}
interface AnotherInterface {}
abstract class SAM3 implements AnotherInterface {
  abstract foo()
}

非 SAM 示例

  • 空接口

interface SomeMarker {}
  • 有两个方法的接口

interface TwoMethods {
  def foo()
  def bar()
}
  • 有两个抽象方法的抽象类

abstract class TwoMethods {
  abstract foo()
  abstract bar()
}
  • 空的抽象类

abstract class Empty {}

对方法选择的影响

正常的 method selection 算法试图找到与给定参数运行时类型最具体的 method,并且对于 null 最通用的 method。由于 SAM 类型和目标方法参数类型之间没有继承关系,“最具体”需要在部分内容上重新定义。假设 SAM 类型类似于给定目标类型的直接子类,但是如果 SAM 类型是 Closure 实现的类型(Runnable 和 Callable),则不需要进行 SAM 强制转换。这种情况在 method selection 中是优先的。如果存在重载方法,其中每个方法都可以用作 SAM 强制转换的目标,那么 method selection 将失败,无论它们的内部关系如何。在 Groovy 中,SAM 类型的实际方法签名和强制转换目标并不重要。此外,目标类型是抽象类还是接口也不重要。

两个 SAM 目标导致运行时方法选择失败的示例

interface SAM1 { def foo(String s)}
interface SAM2 { def bar(Integer i)}
def method(x, SAM1 s1){s1.foo(x)}
def method(x, SAM2 s2){s2.bar(x)}
method (1)   {it}  // fails because SAM1 and SAM2 are seen as equal
method ("1") {it}  // fails because SAM1 and SAM2 are seen as equal

SAM 类型被忽略,因为存在非强制转换情况的示例

interface SAM {def foo(String s)}
def method(SAM s) {1}
def method(Runnable r) {2}
assert method {it} == 2

对静态类型系统的影响

静态类型系统的范围分为基本部分(适用于 Groovy 2.2)和扩展部分(适用于更高版本,例如 2.3 或 3.0)。

Groovy 2.2 静态检查

Groovy 2.2 中的类型检查将限制为模拟普通 Groovy 的行为。不会执行任何方法签名检查,并且不会根据 open block 提供的类型进行任何额外的测试或方法选择。

Groovy 2.2+ 静态检查

在更高版本的 Groovy 中,需要改进静态类型检查器,以便通过 open block 或 lambda 提供的类型签名来细化方法选择。只有当提供的类型和 SAM 中的目标类型在数量和类型本身方面匹配时,SAM 类型才适合进行强制转换。可以在这里找到更详细的描述:http://cr.openjdk.java.net/~dlsmith/jsr335-0.6.1/F.html

更新历史

3 (2013-07-01)

从 Codehaus wiki 中提取的版本

4 (2018-10-29)

大量细微调整