类型擦除导致扩展函数

时间:2015-12-05 12:05:46

标签: scala type-erasure

我编写了以下类,后来我想根据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 })
}

1 个答案:

答案 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

这不会产生任何警告。