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!!!
我理解可堆叠模式,因此第一种情况如何工作。我的问题是为什么第二个没有。
答案 0 :(得分:4)
在第二个片段中,您有一个没有退出条件的递归调用:
override def findByFirstName(firstName: String): Seq[User] = {
self.findByFirstName(firstName)
}
这将始终从findByFirstName
调用UserBusinessDelegate
(因为您正在使用self
,这基本上说这个对象在运行时会有这种行为,而不是它& #39;父母将拥有它,因此我们应该调用父方法)每次调用创建一个新的堆栈帧 - >堆栈溢出。
在第二个代码段UserBusinessDelegate
中findByFirstName
将被调用,然后您使用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
调用该方法。