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

时间:2015-09-08 05:26:54

标签: scala mixins traits self-type

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") // StackOverflow!!!

我理解可堆叠模式,因此第一种情况如何工作。我的问题是为什么第二个没有。

1 个答案:

答案 0 :(得分:4)

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

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

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

在第二个代码段UserBusinessDelegatefindByFirstName将被调用,然后您使用MockUserRepository调用super的方法 - &gt ;没有递归 - >没有堆栈溢出。您可以查看Scala's stackable trait pattern了解详情。

@Edit:为了更清楚,在引发SO异常的代码段中,findByFirstName的{​​{1}}方法不会被调用,因为您在MockUserRepository覆盖它因此,使用UserBusinessDelegate创建的匿名类只包含覆盖方法,这就是SO的原因,那就清楚了吗?

为什么你会假设调用来自new MockUserRepository with UserBusinessDelegate的方法?

@ Edit2:代码在没有MockUserRepository的情况下编译,因为override告诉编译器具有这种签名的方法在运行时已经存在,并且你不能拥有2个方法相同的签名。第一个例子的作用只是因为它是一个可堆叠的特征,这些特征是动态绑定的,可以修改行为,但必须在某个时刻调用self: UserRepository =>(通常不允许{{1}修饰符,我真的建议浏览我发布的关于可堆叠模式的链接。)

也许其他人都知道一种方式,根据我的知识,除非你在super中更改方法名称并放弃abstract override,否则你无法调用模拟方法,那么你可以致电UserBusinessDelegate,它会从override调用该方法。