[英]Force implementation of trait to provide an inner class that the trait can extend
特征是否可能強加要求其實現類實現某個內部類,然后可以對其進行擴展的要求? 例如
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()
我想ThingX
的原因是,我有大量的ThingX
類,所有這些類目前都被迫實現自己的fancyFoo
等效fancyFoo
(以及其他需要將Mixin添加到其特定Foo類的類似方法)。 我想通過將fancyFoo
及其朋友移入BaseTrait來減少樣板,但是我無法提出比現有版本更詳細的內容。
編輯:
我在上面的概括可能掩蓋了總體意圖,所以這里有一些背景知識:
我的實際用例圍繞着對數據庫模式和一些表聯接邏輯進行建模。 團隊開始從Slick的“提升”語法轉移到原始SQL,此系統彈出以幫助支持編寫原始查詢。
Foo
= TableReference
。 每個ThingX
對象代表一個特定的表,它們各自的引用類包含引用該表的列的方法。
SomeMixin
= TableJoin
,應該添加TableJoin
邏輯(即如何從另一個表訪問一個表)。 該ThingX
對象通常定義def direct
獲得直接引用表(即一開始就在FROM
在一個SQL查詢子句),一個def from(someOtherRef)
創建一個INNER JOIN
和def optFrom(someOtherRef)
創建一個LEFT JOIN
。 這三種方法是我試圖抽象到BaseTrait
。
我相信我們確實需要能夠提供普通的TableReference
以及通過TableReference with TableJoin
,因為我們有一個用於組合所有TableReference with TableJoin
邏輯的實用程序,並且我們希望在沒有傳遞任何TableReference with TableJoin
邏輯的情況下禁止引用。 整個代碼庫中有幾種普通引用的用法。
我希望按照以下方式定義一些東西
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 = ???
}
我被上面的最后四種方法困住了,因為它們似乎需要我在原始問題中要求的看似不存在的功能,或者讓TableSupport
實現者顯式定義單獨的方法來創建Reference
和Reference with TableJoin
的Reference with TableJoin
,由於實現這些方法的額外樣板,最終無法達到減少樣板的目的。
我發現的解決方案是包裝類而不是擴展它們,並使用一個隱式解包器讓我與事物進行交互,就像擴展它們一樣。
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
}
由於編譯器無需任何導入即可找到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`
使用這種方法,我能夠實現我希望的direct
, from
和optFrom
方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.