2011-08-08 90 views
33

我正在設計一個類層次結構,它由一個基類和幾個特徵組成。基類提供了幾種方法的默認實現,並且這些特徵通過abstract override有選擇地覆蓋某些方法,以充當可堆疊特徵/混合。如何將特徵聲明爲隱式「構造函數參數」?

從設計的角度來看,這工作得很好,並映射到域,這樣我可以從這裏(一個特徵)與謂詞從這裏(另一個特徵)等

增加過濾功能。然而,現在我我喜歡我的一些特徵來獲取隱式參數。我很高興這從設計的角度來看仍然有意義,並且在實踐中不會令人困惑。但是,我無法說服編譯器運行它。

問題的核心似乎是我無法爲特徵提供構造函數參數,因此它們可能被標記爲隱式。在方法實現中引用隱式參數時,無法編譯期望的「找不到隱式值」消息;我試圖通過

implicit val e = implicitly[ClassName] 

到「傳播」從施工階段(其中,在實踐中,它總是在範圍內)的隱含的是在方法中可用,但(因爲毫無疑問,你們許多人期望的那樣)定義以相同的訊息失敗。

看來這裏的問題是我不能說服編譯器用implicit ClassName標誌標記特徵本身的簽名,並強制調用者(即那些將特徵混合到對象中的調用者)提供隱式。目前我的呼叫者這樣做,但編譯器不檢查此級別。


有沒有什麼辦法來標記性狀爲需要一定implicits可在施工時間?

(如果沒有的話,就是這根本尚未實現,還是有更深層次的原因,這是不切實際的?)

回答

15

其實,我經常之前想這一點,但只是想出了這個主意。您可以翻譯

trait T(implicit impl: ClassName) { 
    def foo = ... // using impl here 
} 

爲[編輯:原版本沒有其他方法可以訪問隱含]

trait T { 
    // no need to ever use it outside T 
    protected case class ClassNameW(implicit val wrapped: ClassName) 

    // normally defined by caller as val implWrap = ClassNameW 
    protected val implWrap: ClassNameW 

    // will have to repeat this when you extend T and need access to the implicit 
    import implWrap.wrapped 

    def foo = ... // using wrapped here 
} 
+0

這是不是讓調用者明確定義'implWrap'雖然在匿名對象中,因爲它是特徵中的抽象字段? (如果沒有,我不明白它是如何設置的;你介意解釋一下嗎?) –

+0

是的,但看到註釋:如果他想使用隱式,他可以只寫'val implWrap = ClassNameW'。我沒有看到一個更好的方法來做到這一點:正如你在問題中提到的,特徵沒有_any_構造函數參數(可能被標記爲隱含的)。 –

+7

當然,我會很高興看到更好的解決方案。 –

0

你可以做這樣的:

abstract class C 

trait A { this: C => 
    val i: Int 
}  

implicit val n = 3 

val a = new C with A { 
    val i = implicitly[Int] 
} 

但我我不確定它是否有任何意義 - 你可以明確地引用隱含的值。

我想你想要的是在實例化中擺脫i的實現,但正如你自己所說,問題的核心是特徵不需要構造器參數 - 它們是否隱含或不隱含沒關係。

此問題的一個可能的解決方案將是一個新的功能添加到已經有效語法:

trait A { 
    implicit val i: Int 
} 

其中i將被編譯器實現的,如果一個隱含的是在範圍內。

11

我遇到了這個問題幾次,確實有點煩人,但不是太多。抽象成員和參數通常是做同樣事情的兩種替代方式,有其優點和缺點;對於具有抽象成員的特徵不是太不方便,因爲無論如何你需要另一個類來實現這個特徵。因此,你應該簡單地在特徵中有一個抽象值聲明,這樣實現類必須提供一個隱式爲你。請看下面的例子 - 它編譯正確的,並顯示出執行給定性狀的方法有兩種:

trait Base[T] { 
    val numT: Ordering[T] 
} 
/* Here we use a context bound, thus cannot specify the name of the implicit 
* and must define the field explicitly. 
*/ 
class Der1[T: Ordering] extends Base[T] { 
    val numT = implicitly[Ordering[T]] 
    //Type inference cannot figure out the type parameter of implicitly in the previous line 
} 
/* Here we specify an implicit parameter, but add val, so that it automatically 
* implements the abstract value of the superclass. 
*/ 
class Der2[T](implicit val numT: Ordering[T]) extends Base[T] 

的基本想法,我展示的是也存在於克努特阿恩Vedaa的答案,但我試圖讓一個更具吸引力和方便例如,放棄使用不需要的功能。

*這不是特質不能接受參數的原因 - 我不知道。我只是認爲在這種情況下限制是可以接受的。

+2

但是,通過這種方式,您無法在定義Base [T]中的方法時訪問隱式'Ordering [T]'。如果你使'numT'隱式,你修正了_this_問題,但是'val numT =隱式地[Ordering [T]]'變成了一個無限循環。 –

+1

如果修改'Base'來解決這個問題,你不能編寫Der1,但是Der2仍然可以工作,並且在等價的情況下比Der1更緊湊。 特性Base [T]隱含val numT:排序[T] } 類Der2 [T](隱式val numT:排序[T])擴展Base [T]' – Blaisorblade

0

因爲看起來這是不可能的,所以我選擇在基類的構造函數中聲明隱含的val。正如在問題中指出的那樣,這不是理想的,但它使編譯器滿意,並且在我的特殊情況下,務實地說,並不是太多的負擔。

如果有人有更好的解決方案,但我很樂意聽到並接受它。

7

這是不可能的。

但是,您可以使用implicitly和Scala的類型推斷來使其儘可能無痛苦。

trait MyTrait { 

    protected[this] implicit def e: ClassName 

} 

然後

class MyClass extends MyTrait { 

    protected[this] val e = implicitly // or def 

} 

簡潔,甚至不需要寫在擴展類的類型。

相關問題