Scala +模式匹配+字符串自动装箱

时间:2016-05-13 07:12:30

标签: scala pattern-matching

我报告的一个功能听起来很奇怪,并且无法在scala中使用模式匹配来解释以下行为。

def typesPattern(x:Any)= x match{
    case s:String⇒ s.length
    case n:Map[Int,Int]⇒println("Map[Int,Int]");var a =  n.iterator.next();println(a._1);println(a._2);n.size;      
    case n:Map[a,b]⇒println("Map[a,b]");n.size;     
    case m:Map[_,_]⇒ m.size
    case _ ⇒ -1

    }
}

当我使用以下println(typesPattern(Map("a"→10)))调用上述函数时,我收到以下错误Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:101) at scala.Tuple2._1$mcI$sp(Tuple2.scala:20)

我的第一个问题是“为什么MAP [String-> Int]与MAP [INT,INT]匹配?”,它应该是与MAP[_,_]匹配。

有趣的是,当我编辑模式匹配代码并取出从Map中提取元组并打印键和值对的代码时

`def typesPattern(x:Any)= x match{
        case s:String⇒ s.length
        case n:Map[Int,Int]⇒println("Map[Int,Int]");n.size;     
        case n:Map[a,b]⇒println("Map[a,b]");n.size;     
        case m:Map[_,_]⇒ m.size
        case _ ⇒ -1

        }
    }`

现在像之前的println(typesPattern(Map("a"→10)))一样的调用确实匹配MAP [INT,INT]而没有问题并打印大小。

Map[Int,Int] 
    1

第二个问题 “为什么这个时间SCALA可以与MAP [INT-> INT]匹配MAP [String-> INT](我仍然想知道怎么做?)没有问题?

4 个答案:

答案 0 :(得分:2)

您可能会尝试查看编译器提供的警告吗?

<console>:12: warning: non-variable type argument Int in type pattern scala.collection.immutable.Map[Int,Int] (the underlying of Map[Int,Int]) is unchecked since it is eliminated by erasure
       case n:Map[Int,Int]⇒println("Map[Int,Int]");var a =  n.iterator.next();println(a._1);println(a._2);n.size;
              ^
<console>:13: warning: unreachable code
       case n:Map[a,b]⇒println("Map[a,b]");n.size;

实际上,这两行都是:

    case n:Map[a,b]⇒println("Map[a,b]");n.size;     
    case m:Map[_,_]⇒ m.size

无法访问,因为在地图上匹配的所有三条线都是等效的,至少它们的模式会匹配相同的东西。

在运行时没有泛型类型,它们会被删除,因此Map[A, B]只是Map。因此,匹配地图的唯一情况是第一个,因为它们按顺序进行测试

case n:Map[Int,Int]⇒println("Map[Int,Int]");var a =  n.iterator.next();println(a._1);println(a._2);n.size;      

只有当您尝试使用像ClassCastException一样处理它们的值时才会得到Int,因为只有在您尝试使用它们时才会进行转换。检查size不依赖于其值的类型。

答案 1 :(得分:1)

由于泛型类型擦除,会出现此问题。在运行时,任何类型的Map之间没有区别。这就是为什么模式在第一个合适的情况下匹配的原因。

要检查的简单代码段:

List[String]().isInstanceOf[List[String]]    // true
List[String]().isInstanceOf[List[Integer]]   // true

答案 2 :(得分:0)

这是因为类型擦除。泛型类型的使用在case子句中没有用,因为它不保留类型信息。因此MAP[String->Int]相当于Map。这就是MAP[String->Int]MAP[Int->Int]匹配的原因。

答案 3 :(得分:0)

如果不是尝试使用模式匹配,而是使用implicits和类型类机制,那会不会更容易?

trait TypePattern[A,B] {
  def pattern(a: A):B
}

implicit object stringPattern extends TypePattern[String,Int] {
  override def pattern(a: String): Int = a.length
}

implicit object mapIntIntPattern extends TypePattern[Map[Int, Int],Int] {
  override def pattern(n: Map[Int, Int]): Int = {
    println("Map[Int,Int]")
    var a =  n.iterator.next()
    println(a._1)
    println(a._2)
    n.size
  }
}

implicit object mapAnyPattern extends TypePattern[Map[Any, Any],Int] {
  override def pattern(a: Map[Any, Any]): Int = {
    println("Map[a,b]")
    a.size
  }
}

def pattern[A,B](x: A)(implicit typePattern: TypePattern[A,B]): B = {
  typePattern.pattern(x)
}