[英]Implement a final def within some trait where type parameters may or may not be the same
在我的問題之前進行一些設置:
/* roughly equivalent to a union type */
sealed trait NewType
object NewType {
final case class Boolean(record: Boolean) extends NewType
final case class Long(record: Long) extends NewType
final case class String(record: String) extends NewType
}
/* Try to convert a record of type T to a NewType */
sealed trait NewTypeConverter[T] { def apply(record: T): Option[NewType] }
object NewTypeConverter {
trait BooleanConverter[T] extends NewTypeConverter[T] {
override def apply(record: T): Option[NewType.Boolean]
}
trait LongConverter[T] extends NewTypeConverter[T] {
override def apply(record: T): Option[NewType.Long]
}
trait StringConverter[T] extends NewTypeConverter[T] {
override def apply(record: T): Option[NewType.String]
}
}
我想定義一個特征Data
,如下所示:
trait Data[T] {
def name: String
def converter: NewTypeConverter[_]
final def value(record: T): Option[NewType] = ??? // calls converter
}
如何實現此final def value(record: T): Option[NewType]
?
注意事項:
converter
的apply
方法的返回類型必須與value
相同。 因此,如果您碰巧具有BooleanConverter
,則value
必須返回Option[NewValue.Boolean]
。 Data
特征的輸入類型T
不必與converter
的輸入類型_
相同。 如果它們碰巧是同一類型,則實現可能只是final def value(record: T): Option[NewType] = converter(record)
。 輸入類型不同時,情況更棘手。 假設Data
的輸入類型是String
,而converter
的輸入類型是Long
。 如何處理? 看起來您90%的人都通過實現Type Class模式來實現,所以我將嘗試通過完成它來解決您的問題。 這是一篇不錯的文章。 簡而言之,您錯過的是一個簽名,該簽名指出,如果可以在隱式作用域中找到轉換器的一個(只有一個)實現,請使用它來運行轉換(或特征定義的任何其他操作)。
簽名如下:
final def value(record: T)(implicit c: NewTypeConverter[T]): Option[NewType]
給出這樣嚴格的簽名也使實現非常簡單:
final def value(record: T)(implicit c: NewTypeConverter[T]): Option[NewType] =
c(record) // literally only applies `c`, the converter
現在,每當您的轉換器實例在隱式范圍內時,例如以下內容:
implicit val converter: NewTypeConverter[Boolean] =
new StringConverter[Boolean] {
override def apply(record: Boolean): Option[NewType.String] =
if (record) Some(NewType.String("TRUE"))
else Some(NewType.String("FALSE"))
}
您可以實例化您的trait
(在示例中進行了簡化):
trait Data[T] {
def name: String
final def value(record: T)(implicit c: NewTypeConverter[T]): Option[NewType] =
c(record)
}
final case class BooleanData(name: String) extends Data[Boolean]
val bool = BooleanData(name = "foo")
並使用它:
println(bool.value(true)) // prints Some(String(TRUE))
println(bool.value(false)) // prints Some(String(FALSE))
如果您嘗試從無法訪問隱式實例的位置調用value
方法,則會收到錯誤消息:
錯誤:找不到參數轉換器的隱式值:NewTypeConverter [Boolean]
通過隱式提供對象已知功能的證據非常普遍,以至於Scala有一些語法糖,如果您需要提供此類證據(例如,您有一個調用您的value
方法的方法),則可以使用它直接使用它。 它表示如下,與:
通用型之后:
def methodThatCallsValue[T: Data](convertee: T): Option[NewType] =
data.value(convertee)
它稱為上下文綁定 ,它等效於以下內容(在示例中已明確完成):
def methodThatCallsValue(convertee: T)(implicit $ev: Data[T]): Option[NewType] =
data.value(convertee)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.