Scala:为什么使用 self-type 而不是 mixin 会产生 stackoverflow?

Scala: Why using self-type instead of mixin produces stackoverflow?

trait UserRepository {
  def findByFirstName(firstName: String): Seq[User]
}

trait UserBusinessDelegate extends UserRepository {
  abstract override def findByFirstName(firstName: String) = {
    super.findByFirstName(firstName)
  }
}

class MockUserRepository extends UserRepository {    
  override def findByFirstName(firstName: String) = {
    // whatever
  }
}

val userRepository = new MockUserRepository with UserBusinessDelegate

userRepository.findByFirstName("John") // OK

但是,如果我按如下方式更改 UserBusinessDelegate

trait UserBusinessDelegate {
  self: UserRepository =>
  override def findByFirstName(firstName: String): Seq[User] = {
    self.findByFirstName(firstName) // requires explicit return type, thinks this is a recursive call
  }
}

val userRepository = new MockUserRepository with UserBusinessDelegate

userRepository.findByFirstName("John") // Whosebug!!!

我了解可堆叠模式以及第一种情况的工作原理。我的问题是为什么第二个没有。

在第二个片段中,您有一个没有退出条件的递归调用:

override def findByFirstName(firstName: String): Seq[User] = {
    self.findByFirstName(firstName)
}

这将始终从 UserBusinessDelegate 调用 findByFirstName(因为您正在使用 self,这基本上是说该对象在运行时会有这种行为,而不是父级将拥有它,因此我们应该调用父级的方法)每次调用都创建一个新的堆栈帧 -> 堆栈溢出。

在第二个片段中 UserBusinessDelegatefindByFirstName 将被调用,然后您使用 super 从中调用 MockUserRepository 的方法 -> 无递归 ->没有堆栈溢出。您可以查看 Scala's stackable trait pattern 了解更多信息。

@Edit:为了更清楚,在抛出 SO 异常的代码段中,不会调用 MockUserRepository 中的 findByFirstName 方法,因为您在 [=12= 中覆盖了它] 因此,使用 new MockUserRepository with UserBusinessDelegate 创建的匿名 class 将仅包含重写的方法,这就是 SO 的原因,清楚吗?

为什么您会假设来自 MockUserRepository 的方法会被调用?

@Edit2:如果没有 override,代码将无法编译,因为 self: UserRepository => 告诉编译器具有这种签名的方法在运行时已经存在,并且您不能有 2 个方法相同的签名。第一个例子之所以有效,是因为它是一个可堆叠的特征,这样的特征是动态绑定的,可以修改行为但必须在某些时候调用 super (如果没有 abstract override 修饰符,这通常是不允许的,我真的建议阅读 link 我发布的有关可堆叠模式的文章)。

也许其他人知道一种方法,据我所知,除非您更改 UserBusinessDelegate 中的方法名称并删除 override,否则无法调用 mock 方法,然后您可以调用 self.findByFirstName 并且它将调用来自 MockUserRepository 的方法。