[英]How to declare traits as taking implicit “constructor parameters”?
我正在設計一個類層次結構,它由一個基類和幾個特征組成。 基類提供了幾種方法的默認實現,特征通過abstract override
選擇性地覆蓋某些方法,從而充當可堆疊的特征/混合。
從設計的角度來看,這很有效,並映射到域,以便我可以從這里(一個特征)添加一個過濾函數,從這里(另一個特征)添加一個謂詞等。
但是,現在我希望我的一些特征采用隱式參數。 我很高興從設計的角度來看這仍然有意義,並且在實踐中不會造成混淆。 但是,我無法說服編譯器使用它運行。
問題的核心似乎是我無法為特征提供構造函數參數,因此它們可以被標記為隱式。 在方法實現中引用隱式參數無法通過預期的“找不到隱式值”消息進行編譯; 我試圖將隱式從構造階段(在實踐中,它總是在范圍內)“傳播”到在方法中可用
implicit val e = implicitly[ClassName]
但是(毫無疑問,你們中的許多人都期望)該定義因相同的消息而失敗。
似乎這里的問題是我無法說服編譯器使用implicit ClassName
標志標記特征本身的簽名,並強制調用者(即那些將特征混合到對象中的人)提供隱式。 目前我的調用者正在這樣做,但編譯器沒有在這個級別進行檢查。
有什么方法可以將特征標記為要求某些隱式在構建時可用?
(如果沒有,這是否還沒有實施,或者有更深層次的原因為什么這是不切實際的?)
實際上,我以前經常想要這個,但只是想出了這個主意。 你可以翻譯
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
}
這是不可能的。
但是您可以使用implicitly
和 Scala 的類型推斷來使這盡可能輕松。
trait MyTrait {
protected[this] implicit def e: ClassName
}
接着
class MyClass extends MyTrait {
protected[this] val e = implicitly // or def
}
簡潔,甚至不需要在擴展類中編寫類型。
我遇到過幾次這個問題,確實有點煩人,但不是太多。 抽象成員和參數通常是做同一件事的兩種替代方式,各有優缺點。 對於具有抽象成員的特征並不太方便,因為您仍然需要另一個類來實現特征。*
因此,您應該在 trait 中簡單地聲明一個抽象值,以便實現類必須為您提供一個隱式。 請參閱以下示例 - 正確編譯,並顯示了實現給定特征的兩種方法:
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]
我展示的基本思想也出現在 Knut Arne Vedaa 的回答中,但我試圖制作一個更引人注目和更方便的例子,放棄使用不需要的功能。
你可以這樣做:
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
將由編譯器實現。
由於看起來這是不可能的,我選擇了在基類的構造函數上聲明隱式val
的選項。 正如問題中所指出的那樣,這並不理想,但它滿足了編譯器的要求,而且在我的特定情況下,從務實的角度來看,這並沒有太大的負擔。
如果有人有更好的解決方案,我很樂意聽到並接受它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.