[英]Force implementation of trait to provide an inner class that the trait can extend
Is it possible for a trait to impose a requirement that its implementing classes implement some inner class, which it can then extend? 特征是否可能强加要求其实现类实现某个内部类,然后可以对其进行扩展的要求? Eg
例如
trait BaseTrait {
// not actually an "abstract class", but a requirement that
// subclasses provide a class named Foo with this constructor signature
abstract class Foo(bar: Bar)
def normalFoo(bar: Bar): Foo = new Foo(bar)
// trait needs to be able to extend the Foo class implemented by the subclass.
// this seems to be the impossible part, as far as I can tell...
def fancyFoo(bar: Bar): Foo with SomeMixin = new Foo(bar) with SomeMixin {
def anExtraMethod() = println("I'm an extra!")
}
}
object ThingA extends BaseTrait {
class Foo(bar: Bar) {
def getThingAStuff() = println("I'm part of ThingA")
}
}
object ThingB extends BaseTrait {
class Foo(bar: Bar) {
def getThingBStuff() = println("I'm part of ThingB")
}
}
// calling `fancyFoo` on the concrete implementations should grant
// access to the specific methods in their respective `Foo` classes,
// as well as the "extra method" that the trait adds
val aFoo: ThingA.Foo with SomeMixin = ThingA.fancyFoo(bar)
aFoo.getThingAStuff()
aFoo.anExtraMethod()
val bFoo: ThingB.Foo with SomeMixin = ThingB.fancyFoo(bar)
bFoo.getThingBStuff()
bFoo.anExtraMethod()
The reason I want this is that I've got a large number of ThingX
classes which all are currently forced to implement their own equivalent of fancyFoo
(and other similar methods that require a Mixin to be added to their specific Foo class). 我想
ThingX
的原因是,我有大量的ThingX
类,所有这些类目前都被迫实现自己的fancyFoo
等效fancyFoo
(以及其他需要将Mixin添加到其特定Foo类的类似方法)。 I want to cut down on the boilerplate by moving fancyFoo
and its friends into the BaseTrait, but I've been unable to come up with anything less-verbose than what's already there. 我想通过将
fancyFoo
及其朋友移入BaseTrait来减少样板,但是我无法提出比现有版本更详细的内容。
edit: 编辑:
My generalization above may have obscured the overall intent, so here's some background: 我在上面的概括可能掩盖了总体意图,所以这里有一些背景知识:
My actual use case revolves around modelling a database schema and some table join logic. 我的实际用例围绕着对数据库模式和一些表联接逻辑进行建模。 The team started moving away from Slick's "lifted" syntax and more towards raw sql, and this system popped up to help support writing raw queries.
团队开始从Slick的“提升”语法转移到原始SQL,此系统弹出以帮助支持编写原始查询。
Foo
= TableReference
. Foo
= TableReference
。 Each of the ThingX
objects represent a particular table, and their respective reference classes contain methods to reference that table's columns. 每个
ThingX
对象代表一个特定的表,它们各自的引用类包含引用该表的列的方法。
SomeMixin
= TableJoin
, which was supposed to add join logic (ie how to reach one table from another table). SomeMixin
= TableJoin
,应该添加TableJoin
逻辑(即如何从另一个表访问一个表)。 The ThingX
objects typically define a def direct
to get a direct reference to the table (ie the start of a the FROM
clause in a SQL query), a def from(someOtherRef)
that creates an INNER JOIN
, and a def optFrom(someOtherRef)
that creates a LEFT JOIN
. 该
ThingX
对象通常定义def direct
获得直接引用表(即一开始就在FROM
在一个SQL查询子句),一个def from(someOtherRef)
创建一个INNER JOIN
和def optFrom(someOtherRef)
创建一个LEFT JOIN
。 Those three methods are what I was trying to abstract away into the BaseTrait
. 这三种方法是我试图抽象到
BaseTrait
。
I believe we do need to be able to provide a plain TableReference
as well as provide a TableReference with TableJoin
, as we have a utility for combining all of the join logic and we want to forbid references without any join logic from being passed into it. 我相信我们确实需要能够提供普通的
TableReference
以及通过TableReference with TableJoin
,因为我们有一个用于组合所有TableReference with TableJoin
逻辑的实用程序,并且我们希望在没有传递任何TableReference with TableJoin
逻辑的情况下禁止引用。 There are several usages of plain references throughout the codebase. 整个代码库中有几种普通引用的用法。
I'm hoping to define something along the lines of 我希望按照以下方式定义一些东西
trait TableSupport {
type Reference <: TableReference
trait CanMatch[Ref] {
// corresponds to the `ON` part of a `JOIN` clause
def matchCondition(self: Reference, other: Ref): RawSQL
}
def defaultAlias: String
// All of the below would be implemented by the `TableSupport` trait
// in terms of the `Reference` constructor and mixing in TableJoin.
// But currently each table companion has to explicitly implement these.
def reference(alias: String = defaultAlias): Reference = ???
def direct(alias: String = defaultAlias): Reference with TableJoin = ???
def from[Ref: CanMatch](ref: Ref, alias: String = defaultAlias): Reference with TableJoin = ???
def optFrom[Ref: CanMatch](ref: Ref, alias: String = defaultAlias): Reference with TableJoin = ???
}
I get stuck on the last four methods above, since it seems they require the seemingly-nonexistent feature I ask for in my original question, or for the TableSupport
implementors to explicitly define the separate methods to create a Reference
and a Reference with TableJoin
, which ends up defeating the purpose of reducing boilerplate, because of the additional boilerplate of implementing those methods. 我被上面的最后四种方法困住了,因为它们似乎需要我在原始问题中要求的看似不存在的功能,或者让
TableSupport
实现者显式定义单独的方法来创建Reference
和Reference with TableJoin
的Reference with TableJoin
,由于实现这些方法的额外样板,最终无法达到减少样板的目的。
The solution I found was to wrap the classes rather than extend them, and to use an implicit unwrapper to let me interact with things the same was as if they had been extended. 我发现的解决方案是包装类而不是扩展它们,并使用一个隐式解包器让我与事物进行交互,就像扩展它们一样。
SomeTableRef with TableJoin
becomes TableJoin[SomeTableRef]
ie SomeTableRef with TableJoin
变为TableJoin[SomeTableRef]
即
class TableJoin[T <: TableReference](val self: T, val joinStep: RawSQL)
object TableJoin {
import language.implicitConversions
implicit def unwrap[T <: TableReference](tj: TableJoin[T]): T = tj.self
}
Since the compiler is able to find the unwrap
method for a TableJoin[T] without any imports, I can treat it the same as if it were a mixin: 由于编译器无需任何导入即可找到TableJoin [T]的
unwrap
方法,因此可以将其视为mixin一样对待:
class SomeTableRef(alias: String) extends TableReference {
def id = column("ID")
}
val joinedRef = new TableJoin(new SomeTableRef(defaultAlias), /* raw sql */)
joinedRef.id // compiles fine because of `unwrap`
Using this approach, I was able to implement the direct
, from
and optFrom
methods as I had hoped. 使用这种方法,我能够实现我希望的
direct
, from
和optFrom
方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.