我编写了以下类,后来我想根据Pimp My Library模式使用它:
class ListX[A] (list: List[A]) {
def zipMap[A, B, C] (that: List[B], op: (A, B) => C): List[C] =
list.zip(that).map({
case (a: A, b: B) => op(a, b)
})
}
这会收集警告:
[warn] /src/main/scala/ListX.scala:8: abstract type pattern A is unchecked since it is eliminated by erasure
[warn] case (a: A, b: B) => op(a, b)
[warn] ^
[warn] /src/main/scala/ListX.scala:8: abstract type pattern B is unchecked since it is eliminated by erasure
[warn] case (a: A, b: B) => op(a, b)
[warn] ^
[warn] two warnings found
在REPL中测试会导致以下错误:
scala> val a = new ListX(List(1, 1, 1))
scala> val b = List(1, 1, 1)
scala> val result = a.zipMap(b, _ + _)
error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2))
scala> val expected = List(2, 2, 2)
由于我是Scala的新手,我不完全理解警告和错误。我知道有一个名为&#34;类型擦除&#34;,但不是它如何工作,我可以看到这可能导致缺少类型。
那有什么问题,如何纠正呢?
更新
感谢接受的答案及其评论,我设法解决了问题,并按如下方式重写了课程:
implicit class ListX[A] (list: List[A]) {
def zipMap[B, C] (that: List[B])(op: (A, B) => C): List[C]
= list.zip(that).map({ op.tupled })
}
答案 0 :(得分:4)
您的代码中存在许多问题,不仅仅是类型擦除:
为了让scala派生lambda的类型,你需要将它放在一个单独的参数组中:(that: List[B])(op: (A, B) => C)
然后像zipMap(List(1, 1, 1))(_ + _)
一样调用它。你可以在这里找到很多关于此的答案。简而言之,这是因为scala不知道您定义它的组中的类型B
,因此您指定类型的第一个组无法推断出类型
然后您定义了两次类型参数A
。一个在你班上,一个在你的方法中。你只需要一次。 Scala将这两个A
理解为两个不同的类型参数,因此后者A
没有任何实际类型,因为它与list: List[A]
无关,并且scala无法为{派生类型{1}}。
你需要一个_ + _
上下文绑定你的类型参数来对抗类型擦除。它写为ClassTag
简而言之,这允许scala附加[A : ClassTag]
和A
的类型信息,以便在scala匹配中使用它。
结果代码是:
(请参阅答案末尾的代码作为更好的版本,这个可以简化)
B
<强>更新强>
在想一下@Łukasz的评论后,我意识到我犯了一个错误:你真的不需要 class ListX[A : ClassTag](list: List[A]) {
def zipMap[B: ClassTag, C](that: List[B])(op: (A, B) => C): List[C] =
list.zip(that).map({
case (a: A, b: B) => op(a, b)
})
}
println(new ListX[Int](List(2, 2, 2)).zipMap(List(1, 1, 1))(_ + _))
。生成警告的原因是您指定ClassTag
应明确显示类型case
。相反,你可以让编译器有机会自己弄清楚这一点,如果我理解这里正在发生的事情 - 编译器可以发现你在这里不需要(A, B)
,因为它们什么都不做。见:
ClassTag
这不会产生任何警告。