簡體   English   中英

強制執行特質以提供該特質可以擴展的內部類

[英]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 JOINdef 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實現者顯式定義單獨的方法來創建ReferenceReference with TableJoinReference 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`

使用這種方法,我能夠實現我希望的directfromoptFrom方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM