我已经读过几篇文章,表示应该使用抽象类型来实现Scala中的f-bounded多态。这主要是为了减轻类型推断问题,同时也消除了类型参数在定义递归类型时似乎引入的二次增长。
这些定义如下:
trait EventSourced[E] {
self =>
type FBound <: EventSourced[E] { type FBound <: self.FBound }
def apply(event: E): FBound
}
然而,这似乎引入了两个问题:
1)每次用户想要引用此类型的对象时,他们还必须引用FBound
类型参数。这感觉就像代码味道:
def mapToSomething[ES <: EventSourced[E], E](eventSourced: ES#FBound): Something[ES, E] = ...
2)编译器现在无法推断上述方法的类型参数,但没有消息:
Type mismatch, expected: NotInferredES#FBound, actual: MyImpl#FBound
是否有人在其解决方案中使用f-bounded多态的成功实现,编译器仍能推断出类型?
答案 0 :(得分:3)
我已经意识到在大多数情况下应该避免使用f-bounded多态 - 或者更确切地说 - 这通常是你应该选择的替代设计。要了解如何避免它,我们首先需要知道是什么让我们需要它:
当类型期望在派生类型中引入重要接口更改时,会发生F-bounded多态。
通过编写预期的更改区域而不是尝试通过继承来支持它们,可以避免这种情况。这实际上回到了Gang of Four设计模式:
偏好&#39;对象组合&#39;过度继承&#39;
- (Gang of Four,1995)
例如:
SWIFT_VERSION
变为:
trait Vehicle[V <: Vehicle[V, W], W] {
def replaceWheels(wheels: W): V
}
在这里,预期的变化&#39;是车型(例如trait Vehicle[T, W] {
val vehicleType: T
def replaceWheels(wheels: W): Vehicle[T, W]
}
,Bike
,Car
)。前面的示例假设这将通过继承添加,需要一个f-bounded类型,使得使用Vehicle的任何函数都无法推断Lorry
。使用合成的新方法不会出现此问题。
答案 1 :(得分:-1)
答案 2 :(得分:-1)
我可能会遗漏以下实施内容。
trait EventSourced[E] {
self =>
type FBound <: EventSourced[E] { type FBound <: self.FBound }
def apply(event: E): FBound
}
trait Something[ES, E]
def mapToSomething[E](
eventSourced: ES forSome {
type ES <: EventSourced[E]
}): Something[eventSourced.type, E] = ???
class Test extends EventSourced[Boolean] {
type FBound = Test
def apply(event:Boolean):FBound = ???
}
val x:Test = ???
mapToSomething(x)