Scala:为泛型SeqLike编写隐式类报错,求问题排查方案
Scala泛化SeqLike隐式类的类型推导错误解决
问题背景
最初为List实现了隐式扩展类,提供replace、insert、removeAt等操作:
implicit class ListEnricher[T](list: List[T]) { def replace(i: Int)(f: T => T): List[T] = list.patch(i, List(f(list(i))), 1) def replace(i: Int, t: T, extend: Boolean = false): List[T] = if (extend && i >= list.size) insert(i, t) else replace(i)(_ => t) def insert(i: Int, t: T): List[T] = list.patch(i, List(t), 0) def removeAt(i: Int): List[T] = list.patch(i, List(), 1) }
为了让这些操作支持所有SeqLike类型(如Seq),泛化后的隐式类定义如下:
import scala.collection.SeqLike import scala.collection.generic.CanBuildFrom implicit class SeqLikeEnricher[T, S <: SeqLike[T, S]](seq: S) { def replace(i: Int)(f: T => T)(implicit bf: CanBuildFrom[S, T, S]): S = seq.patch[T, S](i, Seq(f(seq(i))), 1) def replace(i: Int, t: T, extend: Boolean = false)(implicit bf: CanBuildFrom[S, T, S]): S = if (extend && i >= seq.size) insert(i, t) else replace(i)(_ => t) def insert(i: Int, t: T)(implicit bf: CanBuildFrom[S, T, S]): S = seq.patch[T, S](i, Seq(t), 0) def removeAt(i: Int)(implicit bf: CanBuildFrom[S, T, S]): S = seq.patch[T, S](i, Seq(), 1) }
但调用时出现编译错误:
val seq = Seq(1, 2, 3, 4) SeqLikeEnricher(seq).replace(1, 2)
Inferred type arguments [Nothing,Seq[Int]] do not conform to method SeqLikeEnricher's type parameter bounds [T,S <: scala.collection.SeqLike[T,S]]
错误原因
类型约束S <: SeqLike[T, S]要求S必须是SeqLike实例,且元素类型为T、自身类型为S。但Seq[Int]是抽象类型,编译器无法自动从Seq[Int]推断出对应的T类型,导致T被推断为Nothing,违反了类型约束。
另外,隐式类的设计目的是让你直接通过seq.replace(...)调用,无需显式构造SeqLikeEnricher实例。
解决方法
方法1:调整类型约束(推荐)
将类型约束改为S <: Seq[T],编译器可直接从S的元素类型推断出T,同时兼容所有Seq及其子类:
import scala.collection.generic.CanBuildFrom implicit class SeqEnricher[T, S <: Seq[T]](seq: S) { def replace(i: Int)(f: T => T)(implicit bf: CanBuildFrom[S, T, S]): S = seq.patch(i, Seq(f(seq(i))), 1) def replace(i: Int, t: T, extend: Boolean = false)(implicit bf: CanBuildFrom[S, T, S]): S = if (extend && i >= seq.size) insert(i, t) else replace(i)(_ => t) def insert(i: Int, t: T)(implicit bf: CanBuildFrom[S, T, S]): S = seq.patch(i, Seq(t), 0) def removeAt(i: Int)(implicit bf: CanBuildFrom[S, T, S]): S = seq.patch(i, Seq(), 1) }
调用时直接使用隐式扩展的方法即可:
val seq = Seq(1, 2, 3, 4) val updatedSeq = seq.replace(1, 2) // 正常编译
方法2:显式指定类型参数(临时方案)
如果坚持使用原SeqLike约束,调用时显式指定T和S的类型:
val seq = Seq(1, 2, 3, 4) val updatedSeq = SeqLikeEnricher[Int, Seq[Int]](seq).replace(1, 2)
内容的提问来源于stack exchange,提问作者user79074
相关产品推荐
相关产品推荐

