【问题标题】:Scala: Why using self-type instead of mixin produces stackoverflow?Scala:为什么使用 self-type 而不是 mixin 会产生 stackoverflow?
【发布时间】:2015-12-03 16:02:57
【问题描述】:
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!!!

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

【问题讨论】:

    标签: scala mixins traits self-type


    【解决方案1】:

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

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

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

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

    @Edit:为了更清楚,在引发 SO 异常的 sn-p 中,不会调用来自 MockUserRepositoryfindByFirstName 方法,因为您在 UserBusinessDelegate 中覆盖了它,因此创建了匿名类new MockUserRepository with UserBusinessDelegate 将只包含被覆盖的方法,这就是为什么 SO,清楚吗?

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

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

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

    【讨论】:

    • 我不认为你如何解释堆栈溢出是完全正确的。 UserBusinessDelegate 不会无限地调用自己。我了解可堆叠模式,因此了解第一种情况的工作原理。我的问题是为什么第二个没有。我期望在第二种情况下发生的是UserBusinessDelegate,称为MockUserRepository。实际发生的是匿名类new MockUserRepository with UserBusinessDelegateUserBusinessDelegate 之间来回调用。 MockUserRepository 永远不会被调用。
    • @AbhijitSarkar 请看我的编辑,你正在覆盖MockUserRepository的方法,所以它永远不会被调用
    • 所以在 mixin 的情况下,左边的第一个被调用 (MockUserRepository) 但在 self-type 的情况下,最后一个获胜 (UserBusinessDelegate),即使 MockUserRepository 有一个findByFirstName 的实现?如果我不在UserBusinessDelegate 中覆盖,则代码不会编译。是否可以使用 self 类型以某种方式从 MockUserRepository 调用该方法?
    • 很好的解释。我赞成你的回答。我会等着看别人有没有什么要说的再接受。
    猜你喜欢
    • 2021-06-05
    • 2018-03-17
    • 1970-01-01
    • 2015-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-14
    • 2017-12-06
    相关资源
    最近更新 更多