Scala模式匹配泛型类型擦除问题
Posted 过往记忆大数据
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala模式匹配泛型类型擦除问题相关的知识,希望对你有一定的参考价值。
在中一个很强大的功能就是模式匹配,本文并不打算介绍模式匹配的概念以及如何使用。本文的主要内容是讨论模式匹配泛型类型擦除问题。先来看看泛型类型擦除是什么情况:
scala> def test(a:Any) = a match {
| case a :List[String] => println("iteblog is ok");
| case _ =>
|}
按照代码的意思应该是匹配List[String]类型的数据,但是这个test(List("Iteblog")) 和 test(List(2015))都是可以通过的。
1 |
scala> test(List( "Iteblog" )) |
4 |
scala> test(List( 2015 )) |
这是因为类型被擦除了,你在编译这个代码的时候也会看到编译器给的提示:
1 |
<console> : 14 : warning : non-variable type argument String in type pattern List[String] is unchecked since it is eliminated by erasure |
2 |
case a : List[String] = > println( "iteblog is ok" ); |
上面测试结果都得到了结果,虽然不是我们要的,但是并没有出现什么异常。下面的代码却会出现异常:
scala> case class Container[+A](value: A)
defined class Container
scala> val double = Container(3.3)
double: Container[Double] = Container(3.3)
scala> double match {
| case c: Container[String] => println(c.value.toUpperCase)
| case c: Container[Double] => println(math.sqrt(c.value))
| case _ => println("_")
| }
因为类型信息被编译器擦除了,所以你的double虽然是Container[Double]类型的,但是只能匹配到第一个case,而你想把一个double转换成String,所以会出现以下的异常:
01 |
java.lang.ClassCastException : java.lang.Double cannot be cast to java.lang.String |
02 |
at $iwC$$iwC$$iwC$$iwC$$iwC$$iwC.<init>(<console> : 19 ) |
03 |
at $iwC$$iwC$$iwC$$iwC$$iwC.<init>(<console> : 27 ) |
04 |
at $iwC$$iwC$$iwC$$iwC.<init>(<console> : 29 ) |
05 |
at $iwC$$iwC$$iwC.<init>(<console> : 31 ) |
06 |
at $iwC$$iwC.<init>(<console> : 33 ) |
07 |
at $iwC.<init>(<console> : 35 ) |
08 |
at <init>(<console> : 37 ) |
09 |
at .<init>(<console> : 41 ) |
10 |
at .<clinit>(<console>) |
11 |
at .<init>(<console> : 7 ) |
12 |
at .<clinit>(<console>) |
14 |
at sun.reflect.NativeMethodAccessorImpl.invoke 0 (Native Method) |
15 |
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java : 57 ) |
16 |
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java : 43 ) |
17 |
at java.lang.reflect.Method.invoke(Method.java : 606 ) |
18 |
at org.apache.spark.repl.SparkIMain$ReadEvalPrint.call(SparkIMain.scala : 1065 ) |
19 |
at org.apache.spark.repl.SparkIMain$Request.loadAndRun(SparkIMain.scala : 1340 ) |
20 |
at org.apache.spark.repl.SparkIMain.loadAndRunReq$ 1 (SparkIMain.scala : 840 ) |
21 |
at org.apache.spark.repl.SparkIMain.interpret(SparkIMain.scala : 871 ) |
22 |
at org.apache.spark.repl.SparkIMain.interpret(SparkIMain.scala : 819 ) |
23 |
at org.apache.spark.repl.SparkILoop.reallyInterpret$ 1 (SparkILoop.scala : 857 ) |
24 |
at org.apache.spark.repl.SparkILoop.interpretStartingWith(SparkILoop.scala : 902 ) |
25 |
at org.apache.spark.repl.SparkILoop.reallyInterpret$ 1 (SparkILoop.scala : 875 ) |
26 |
at org.apache.spark.repl.SparkILoop.interpretStartingWith(SparkILoop.scala : 902 ) |
27 |
at org.apache.spark.repl.SparkILoop.reallyInterpret$ 1 (SparkILoop.scala : 875 ) |
28 |
at org.apache.spark.repl.SparkILoop.interpretStartingWith(SparkILoop.scala : 902 ) |
29 |
at org.apache.spark.repl.SparkILoop.reallyInterpret$ 1 (SparkILoop.scala : 875 ) |
30 |
at org.apache.spark.repl.SparkILoop.interpretStartingWith(SparkILoop.scala : 902 ) |
31 |
at org.apache.spark.repl.SparkILoop.reallyInterpret$ 1 (SparkILoop.scala : 875 ) |
32 |
at org.apache.spark.repl.SparkILoop.interpretStartingWith(SparkILoop.scala : 902 ) |
33 |
at org.apache.spark.repl.SparkILoop.command(SparkILoop.scala : 814 ) |
34 |
at org.apache.spark.repl.SparkILoop.processLine$ 1 (SparkILoop.scala : 657 ) |
35 |
at org.apache.spark.repl.SparkILoop.innerLoop$ 1 (SparkILoop.scala : 665 ) |
36 |
at org.apache.spark.repl.SparkILoop.org$apache$spark$repl$SparkILoop$$loop(SparkILoop.scala : 670 ) |
37 |
at org.apache.spark.repl.SparkILoop$$anonfun$org$apache$spark$repl$SparkILoop$$process$ 1 .apply$mcZ$sp(SparkILoop.scala : 997 ) |
38 |
at org.apache.spark.repl.SparkILoop$$anonfun$org$apache$spark$repl$SparkILoop$$process$ 1 .apply(SparkILoop.scala : 945 ) |
39 |
at org.apache.spark.repl.SparkILoop$$anonfun$org$apache$spark$repl$SparkILoop$$process$ 1 .apply(SparkILoop.scala : 945 ) |
40 |
at scala.tools.nsc.util.ScalaClassLoader$.savingContextLoader(ScalaClassLoader.scala : 135 ) |
41 |
at org.apache.spark.repl.SparkILoop.org$apache$spark$repl$SparkILoop$$process(SparkILoop.scala : 945 ) |
42 |
at org.apache.spark.repl.SparkILoop.process(SparkILoop.scala : 1059 ) |
43 |
at org.apache.spark.repl.Main$.main(Main.scala : 31 ) |
44 |
at org.apache.spark.repl.Main.main(Main.scala) |
45 |
at sun.reflect.NativeMethodAccessorImpl.invoke 0 (Native Method) |
46 |
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java : 57 ) |
47 |
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java : 43 ) |
48 |
at java.lang.reflect.Method.invoke(Method.java : 606 ) |
49 |
at org.apache.spark.deploy.SparkSubmit$.org$apache$spark$deploy$SparkSubmit$$runMain(SparkSubmit.scala : 673 ) |
50 |
at org.apache.spark.deploy.SparkSubmit$.doRunMain$ 1 (SparkSubmit.scala : 180 ) |
51 |
at org.apache.spark.deploy.SparkSubmit$.submit(SparkSubmit.scala : 205 ) |
52 |
at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala : 120 ) |
53 |
at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala) |
那如何来处理这种问题呢?其实你可以用下面的代码解决:
1 |
def matchContainer[A : Manifest](c : Container[A]) = c match { |
2 |
case c : Container[String] if manifest < : < manifest[String] = > println(c.value.toUpperCase) |
3 |
case c : Container[Double] if manifest < : < manifest[Double] = > println(math.sqrt(c.value)) |
4 |
case c : Container[ _ ] = > println( "other" ) |
它可以判断出c的类型,但是Manifest已经被标记为deprecated,我们可以使用TypeTag替代Manifest:
1 |
import reflect.runtime.universe. _ |
2 |
def matchContainer[A : TypeTag](c : Container[A]) = c match { |
3 |
case c : Container[String] if typeOf[A] < : < typeOf[String] = > println(c.value.toUpperCase) |
4 |
case c : Container[Double] if typeOf[A] < : < typeOf[Double] = > println(math.sqrt(c.value)) |
5 |
case c : Container[ _ ] = > println( "other" ) |
不过TypeTags 是线程不安全的,可以参见:http://docs.scala-lang.org/overviews/reflection/thread-safety.html。
如果在多线程环境下,我们不能使用上面的代码。我们可以用下面代码解决:
1 |
var container : Container[Any] = double |
3 |
case Container(x : String) = > println( "string" ) |
4 |
case Container(x : Double) = > println( "double" ) |
5 |
case _ = > println( "iteblog" ) |
以上是关于Scala模式匹配泛型类型擦除问题的主要内容,如果未能解决你的问题,请参考以下文章
在Scala中对列表/序列进行模式匹配时解决类型擦除问题
Java泛型:类型擦除
Scala的类与类型
scala中的@specialized
Scala:抽象类型模式 A 未选中,因为它已被擦除消除
Scala:基础知识03