我正在嘗試從具有泛型型別的方法中呼叫 PureConfig 的 loadOrThrow:
def load[T: ClassTag](path: String): T = {
import pureconfig.generic.auto._
ConfigSource.file(path).loadOrThrow[T]
}
當我嘗試從主類呼叫它時,出現以下錯誤:
could not find implicit value for parameter reader: pureconfig.ConfigReader[T]
ConfigSource.file(path).loadOrThrow[T]
我可以import pureconfig.generic.auto._在沒有主課的情況下解決這個問題嗎 ?
uj5u.com熱心網友回復:
總結評論并解釋這個編解碼器是如何作業的。
當你這樣做時:
def something[T: ConfigReader] = ...
你正在使用語法糖
// Scala 2
def something[T](implicit configReader: ConfigReader[T]) = ...
// Scala 3
def something[T](using configReader: ConfigReader[T]) = ...
在您寫信時的呼叫站點上:
something[T]
編譯器實際上是
something(configReaderForT /* : ConfigReader[T] */)
所以基本上它是編譯器支持的基于型別的依賴注入。并且依賴注入必須從某個地方獲取要傳遞的值。
編譯器如何獲取該值以傳遞它?它必須在范圍內按其型別找到它。應該有一個def標記為implicit(Scala 2) 或given(Scala 3)的這種型別的明確最接近的值(或回傳此值)。
// Scala 2
implicit val fooConfigReader: ConfigReader[Foo] = ...
something[Foo] // will use fooConfigReader
// Scala 3
given fooConfigReader: ConfigReader[Foo] = ...
something[Foo] // will use fooConfigReader
Scala 3 基本上更容易區分哪個是價值的定義
given- 哪個是依賴于從外部某個地方提供價值的地方 -using。Scala 2 有一個詞來形容它——implicit這是很多混亂的根源。
您必須自己定義此值/方法或匯入它 - 在需要它的范圍內 - 否則編譯器只會嘗試查看對您的型別有貢獻的所有型別的伴隨物件T- 如果T是特定的。(或者如果在編譯器錯誤訊息中找不到它,則失敗)。
// Example of companion object approach
// This is type Foo
case class Foo()
// This is Foo's companion object
object Foo {
// This approach (calling derivation manually) is called semiauto
// and it usually needs a separate import
import pureconfig.generic.semiauto._
implicit val configReader: ConfigReader[Foo] = deriveReader[Foo]
}
// By requiring ConfigReader[Foo] (if it wasn't defined/imported
// into the scope that needs it) compiler would look into:
// * ConfigReader companion object
// * Foo companion object
// ConfigReader doesn't have such instance but Foo does.
如果T是通用的,那么您必須將該implicit/given作為引數傳遞- 但是您只是推遲必須指定它并讓編譯器找到/生成它的時刻。
// Tells compiler to use value passed as parameter
// as it wouldn't be able to generate it based on generic information
// implicit/using expressed as "type bounds" (valid in Scala 2 and 3)
def something[T: ConfigReader] = ...
// Scala 2
def something[T](implicit configReader: ConfigReader[T]) = ...
// Scala 3
def something[T](using configReader: ConfigReader[T]) = ...
// It works the same for class constructors.
在 PureConfig 的情況下,pureconfig.generic.autocontains implicit defs 為指定的 生成值T。如果要生成它,則必須將其匯入到需要該特定實體的位置。您可以在伴隨物件中執行此操作,以使其可在此ConfigReader特定型別需要的任何地方自動匯入,或將其匯入 main(或將 T 指定為某物的任何其他位置)。一種方式或其他,你將必須從某個地方獲得,然后添加此[T: ConfigReader]或(implicit configReader: ConfigReader[T])在所有方法簽名這不應該硬編碼T到任何東西。
總結您的選擇是:
- 將匯入保留在主目錄中(如果您在主目錄中硬編碼
T為特定型別) - 派生它并定義為
implicit其他地方,然后從那里匯入它(有些人在traits 中做然后混合它們,但我不喜歡這種方法) - 派生它并
implicit在伴隨物件中定義為
只要您希望您的配置被決議值而不是無型別 JSON (HOCON) 而無需自己撰寫這些編解碼器,您就必須在某處執行該自動(或半自動)派生。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/389719.html
上一篇:使用建構式擴展泛型型別
下一篇:實作協議的Python泛型型別
