我有一個帶有型別引數的自型別注釋的特征。此特征來自庫,無法修改。我想將此特征傳遞給需要型別引數上限的函式。例如,我有這個代碼片段:
sealed trait Job[K] { self =>
type T
}
case class Encoder[T <: Product]()
def encoder(job: Job[_])(implicit ev: job.T <:< Product): Encoder[job.T] =
new Encoder[job.T]()
這將回傳一個錯誤和一個從未使用過Type argument job.T does not conform to upper bound Product
的警告。ev
我應該如何設計encoder
功能?
uj5u.com熱心網友回復:
為什么它不起作用?
您的問題與廣義型別約束無關。您可以將其洗掉,但仍然會出現相同的錯誤。廣義型別約束用于約束方法可以接收的引數型別。
(implicit ev: job.T <:< Product)
提供范圍內僅匹配 if 的證據,僅允許使用引數 wherejob.T <: Product
呼叫方法。這就是它的目的。Job
job.T <: Product
您的問題是因為Encoder
該類具有其型別引數T <: Product
。如您所料,廣義型別約束不會將型別job.T
本身視為 的子型別Product
。證據僅適用于值引數,而不適用于型別本身,因為這就是隱式轉換的作業方式。
例如,假設一個x
型別的值job.T
可以作為引數傳遞給方法:
def encoder(job: Job[_])(x: job.T)(implicit ev: job.T <:< Product): Unit = {
val y: Product = x // expands to: ev.apply(x)
val z: Encoder[Product] = new Encoder[job.T] // does not compile
}
第一行編譯因為x
被擴展為ev.apply(x)
,但第二行不能被擴展,無論是否Encoder
協變。
第一個解決方法
您可以做的一種解決方法是:
def encoder[U <: Product](job: Job[_])(implicit ev: job.T <:< Product): Encoder[U] =
new Encoder[U]()
這樣做的問題是,雖然型別引數U
和T
都是 的子型別Product
,但這個定義并沒有說明它們之間的關系,并且編譯器(甚至 Intellij)不會推斷出正確的結果型別,除非您明確指定它。例如:
val myjob = new Job[Int] {
type T = (Int, Int)
}
val myencoder: Encoder[Nothing] = encoder(myjob) // infers type Nothing
val myencoder2: Encoder[(Int, Int)] = encoder[(Int, Int)](myjob) // fix
但是,job.T <:< Product
如果我們已經有了U <: Product
. 相反,我們可以使用=:=
證據來確保它們的型別相同。
def encoder[U <: Product](job: Job[_])(implicit ev: job.T =:= U): Encoder[U] =
new Encoder[U]()
現在將正確推斷出結果型別。
第二種解決方法
一個更短的解決方法是使用結構型別:
def encoder(job: Job[_] { type T <: Product }): Encoder[job.T] =
new Encoder[job.T]()
這不僅更干凈(不需要通用型別約束),而且還避免了前面的問題。
這兩個版本都適用于 Scala 2.13.8。
uj5u.com熱心網友回復:
擴展 Alin 的答案,您也可以使用型別別名來表達同樣的事情:
type JobProduct[K, P <: Product] = Job[K] { type T = P }
// Here I personally prefer to use a type parameter rather than an existential
// since I have had troubles with those, but if you don't find issues you may just use
// JobProdut[_, P] instead and remove the K type parameter.
def encoder[K, P <: Product](job: JobProduct[K, P]): Encoder[P] =
new Encoder[P]()
這種方法對新手來說可能更具可讀性,并允許重用;但是,與 Alin 所做的基本相同。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/508567.html