泛型 – 更高阶泛型函数中的ClassCastException
我有一个代码尝试将函数包装在另一个执行动态类型检查的函数中:
class Base class Foo extends Base class Bar extends Base object Main{ def checker[A <: Base]( func : A => String) : Base => String = (b : Base) => b match { case a : A => func(a) case _ => "error" } def fooFunc(f : Foo) = "It's a foo" def main(arg : Array[String]) { val check = checker(fooFunc) println(check(new Foo) + "," + check(new Bar)) } } 这会产生以下错误: Exception in thread "main" java.lang.ClassCastException: Bar cannot be cast to Foo at Main$$anonfun$1.apply(Main.scala:17) at Main$.main(Main.scala:19) at Main.main(Main.scala) 如果我删除了type参数并在checker的定义中将A替换为Foo,则效果很好.但是,如果我保留了type参数但省略了function参数并将func(a)替换为“good”,那么我对Foo和Bar都会“好”. 这是什么叫做类型擦除?我不太熟悉这个概念.. 解决方法
是的,你发现自己处于擦除之地.
在第一种情况(原始代码)中,编译器知道A是Foo,但是在运行时,类型参数被擦除到上部类型边界,在此示例中为Base(如果未指定上部类型绑定,type参数被删除为Object).所以JVM看到你的代码如下(注意没有类型参数化): def checker(func: Base => String): Base => String = (b: Base) => b match { case a : Base => func(a) case _ => "error" } 任何Foo或Bar对象都将匹配Base,然后JVM将尝试将其强制转换为Foo并调用func.如果b是类Foo的对象,则工作,但抛出Bar的强制转换异常.没有惊喜. 在第二种情况下,删除type参数并在checker的定义中将A替换为Foo.所以你的代码在运行时看起来像这样(函数不是类型参数化的开头,所以没有删除任何东西): def checker(func: Foo => String): Base => String = (b: Base) => b match { case a: Foo => func(a) case _ => "error" } 这按预期工作,但它固定只检查Foo.因此,您需要为Bar和您要测试的任何其他类型编写单独的检查器. 在第三种情况下,保留类型参数但省略函数参数并将func(a)替换为“good”.这导致类似于情况1的代码(再次从Foo擦除到Base),但是没有调用func,因此不需要转换为Foo.因此,也不例外. def checker: Base => String = (b: Base) => b match { case a: Base => "good" case _ => "error" } 您传递的任何对象(Base的子类)都与第一个子句匹配,并且检查器始终返回“”good“’. 现在的解决方案.你使用Manifest处于正确的轨道上(但你展示的代码对于你想要达到的目标来说太复杂了).当您要求Manifest Scala编译器将其他对象传递给可在运行时用于“恢复”已擦除类型的方法/函数.下面我使用’context bound’而不是拼写出来(隐式清单:Manifest [A]),但它是相同的,只是更短. def checker[A <: Base: Manifest](func: A => String): Base => String = (b: Base) => if(manifest[A].erasure == b.getClass) func(b.asInstanceOf[A]) else "error" 那么当你这样称呼时: def fooFunc(f: Foo) = "It's a foo" def barFunc(f: Bar) = "It's a bar" def main(arg: Array[String]) { val check1 = checker(fooFunc) val check2 = checker(barFunc) println(check1(new Foo) + "," + check1(new Bar)) println(check2(new Foo) + "," + check2(new Bar)) } 您将获得预期的输出: It's a foo,error error,It's a bar Erasure是Scala中各种乐趣的源泉,因为这里的类型参数化比Java更常见.没办法,我建议你可以学习一切. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- 了解SBT,Scala,SBT-Idea和Play框架如何协同工作
- 在更新Angular2 rc3之后,无法绑定到’ngFormMode
- typescript – 使用angular2与visual studio
- 在unix命令行中使用类似Matlab的命令历史记录检索
- twitter-bootstrap – bootstrap模式中的tinymce
- 吴裕雄 Bootstrap 前端框架开发——Bootstrap 字
- angularjs – Angular UI Bootstrap Datepicker
- VIM学习笔记
- 订阅方法不会对更改做出反应[Angular 2]
- Bootstrap mergeCells合并单元格(多列)