我需要实现一个基本上包含键和值对的类,其中值是一系列元组。此元组包含一个对象SynthesizedMetricTag
,以及一个带有Numeric
上下文绑定的类型参数A,因此实际上是一个k / v序列,其中v需要是一个数字。
case class Cohort[A : Numeric](index:Any,values:Seq[(SynthesizedMetricTag,A)])
当我必须实现合并此类的两个实例的函数时,问题就出现了。更具体地说,当我必须将类型A的seq与类型B的seq合并时,问题出现了。两个类型都有Numeric
上下文绑定,所以我的想法是最终得到{{1} }},它符合上下文绑定并合并Cohort[C]
和A
类型序列的所有K / V对,而不重复任何键。
B
当然,此代码会引发以下错误:
case class Cohort[A : Numeric](index:Any,values:Seq[(SynthesizedMetricTag,A)]) {
def merge[B : Numeric, C:Numeric](that:Cohort[B]):Cohort[C] =
if(this.index != that.index) throw new Exception("Unable to merge Cohorts. Criteria is not the same")
else {
val b = new ArrayBuffer[(SynthesizedMetricTag,C)]()
val seen = new mutable.HashSet[SynthesizedMetricTag]()
for (x <- this.values; y <- that.values){
if(!seen(x._1)){
b+= x
seen += x._1
}
if(!seen(y._1)){
b+= y
seen += y._1
}
}
Cohort(this.index,b.toSeq)
}
}
所以我尝试了以下内容:
[error] /Users/ernestrc/dev/everreach/operations-api/src/main/scala/everreach/operations/model/Cohort.scala:14: type mismatch;
[error] found : (everreach.operations.model.SynthesizedMetricTag, A)
[error] required: (everreach.operations.model.SynthesizedMetricTag, C)
[error] b+= x
[error] ^
[error] /Users/ernestrc/dev/everreach/operations-api/src/main/scala/everreach/operations/model/Cohort.scala:18: type mismatch;
[error] found : (everreach.operations.model.SynthesizedMetricTag, B)
[error] required: (everreach.operations.model.SynthesizedMetricTag, C)
[error] b+= y
它编译,但我确信这不是这种做法的惯用方式。你们知道如何解决这个问题吗?
修改
Alexey提出了一个很好的观点:
如果,例如,您希望发生什么? A是Double,B是Long,和 C是Byte?
好case class Cohort[A : Numeric](index:Any,values:Seq[(SynthesizedMetricTag,A)]) {
def merge[B : Numeric, C:Numeric](that:Cohort[B]):Cohort[C] =
if(this.index != that.index) throw new Exception("Unable to merge Cohorts. Criteria is not the same")
else {
val b = new ArrayBuffer[(SynthesizedMetricTag,C)]()
val seen = new mutable.HashSet[SynthesizedMetricTag]()
for (x <- this.values; y <- that.values){
if(!seen(x._1)){
b+= x.asInstanceOf[(SynthesizedMetricTag,C)]
seen += x._1
}
if(!seen(y._1)){
b+= y.asInstanceOf[(SynthesizedMetricTag,C)]
seen += y._1
}
}
Cohort(this.index,b.toSeq)
}
}
需要作为类型参数传递,否则编译器不知道C
是什么,所以我希望C
和A
成为转换为B
。但我真正想要的是永远不会丢失信息。因此,例如,如果C
是A
且Int
是B
,我希望Float
转换为A
并合并两者Float
和A
到集合B
(C
)。
当然这个层次结构不存在,是吗?这就是我需要将结果类型抽象为Float
并手动将其作为类型参数传递的原因。
答案 0 :(得分:2)
我只是用类型A和B的隐式证据来定义方法,直到C
case class Cohort[A <: Numeric](index:Any,values:Seq[(SynthesizedMetricTag,A)]) {
def merge[B <: Numeric, C](that:Cohort[B])(implicit ev1:A=>C, ev2:B=>C):Cohort[C] =
if(this.index != that.index) throw new Exception("Unable to merge Cohorts. Criteria is not the same")
else {
val b = new ArrayBuffer[(SynthesizedMetricTag,C)]()
val seen = new mutable.HashSet[SynthesizedMetricTag]()
for (x <- this.values; y <- that.values){
if(!seen(x._1)){
b += x._1 -> ev1(x._2)
seen += x._1
}
if(!seen(y._1)){
b+= y._1 -> ev2(y._2)
seen += y._1
}
}
Cohort(this.index,b.toSeq)
}
}