如何在Scala中对泛型类型进行模式匹配?

7 浏览
0 Comments

如何在Scala中对泛型类型进行模式匹配?

假设我们有一个通用类Container

case class Container[+A](value: A)

然后我们想要模式匹配一个包含DoubleAny类型的Container

val double = Container(3.3)  
var container: Container[Any] = double

为了做到这一点,我们通常会这样写:

container match {  
  case c: Container[String] => println(c.value.toUpperCase)
  case c: Container[Double] => println(math.sqrt(c.value))  
  case _ => println("_")  
}

然而,编译器会给出两个警告,分别对应前两个case。例如,第一个警告说:“类型模式Container[String]中的非变量类型参数String未经检查,因为它被擦除。”由于擦除的存在,运行时无法区分不同类型的容器,因此第一个case将被匹配。结果,类型为Container[Double]的容器将被第一个case匹配,而该case实际上是用于匹配Container[String]对象的,因此会在Double上调用toUpperCase方法,从而抛出一个java.lang.ClassCastException异常。

如何匹配特定类型的Container

0
0 Comments

在Scala中,如何对泛型类型进行模式匹配?

问题的出现原因是无法直接对泛型类型进行模式匹配,因为在运行时无法获取泛型类型的具体信息。解决方法是使用isInstanceOf和asInstanceOf方法进行类型检查和类型转换。

下面是一个可能的解决方法,使用isInstanceOf和asInstanceOf方法来进行模式匹配:

container match {  
  case Container(x) if x.isInstanceOf[String] =>  
    println(x.asInstanceOf[String].toUpperCase)  
  case Container(x) if x.isInstanceOf[Double] =>  
    println(math.sqrt(x.asInstanceOf[Double]))  
  case _ => println("_")  
}

然而,这种方法看起来并不优雅。Scala的创建者Martin Odersky教授表示,应该避免使用isInstanceOf和asInstanceOf方法。

Rob Norris在Coursera的课程论坛中指出,根据类型进行模式匹配是一种不好的做法:case foo: Bar => ... 。Scala鼓励充分利用静态类型,并避免在运行时进行类型检查。这与Haskell/ML世界的哲学是一致的。case子句应该匹配构造函数,而不是类型。

为了解决Container匹配问题,可以为每种类型定义一个特殊的容器:

class Container[+A](val value: A)
case class StringContainer(override val value: String)
  extends Container(value)
case class DoubleContainer(override val value: Double)
  extends Container(value)

现在将匹配的对象从类型改为构造函数:

container match {
  case StringContainer(x) => println(x.toUpperCase)
  case DoubleContainer(x) => println(math.sqrt(x))
  case _ => println("_")
}

显然,我们也可以在StringContainer和DoubleContainer两个对象中定义unapply方法,并使用相同的匹配方式,而不是继承Container类:

case class Container[+A](val value: A)
object StringContainer {
  def unapply(c: Container[String]): Option[String] = Some(c.value)
}
object DoubleContainer {
  def unapply(c: Container[Double]): Option[Double] = Some(c.value)
}

然而,由于JVM类型擦除的原因,这种方法无法正常工作。

参考Rob Norris的帖子,可以在这里找到这个答案:https://class.coursera.org/progfun-002/forum/thread?thread_id=842#post-3567。不幸的是,除非你参加了Coursera的课程,否则无法访问。

0
0 Comments

问题的出现原因是在Scala中,当需要对泛型类型进行模式匹配时,无法直接使用通用的类型参数进行匹配。在上述代码中,原始的解决方法是使用Manifest来对泛型类型进行匹配,但是在最新版本的Scala中,Manifest已经被弃用。

解决方法是使用TypeTag来替代Manifest进行泛型类型的模式匹配。在代码中,首先需要导入reflect.runtime.universe._包,然后在matchContainer方法的定义中添加[A: TypeTag]来指定TypeTag作为类型参数。然后,在模式匹配的每个case语句中,将manifest改为typeOf,并使用typeOf[A]来获取泛型类型的Type对象。然后,可以使用<:<运算符来检查类型的关系,并进行相应的操作。

需要注意的是,在最新版本的Scala中,Manifest已被弃用,推荐使用TypeTag来进行泛型类型的模式匹配。

以下是修改后的代码:

import reflect.runtime.universe._
def matchContainer[A: TypeTag](c: Container[A]) = c match {
  case c: Container[String] if typeOf[A] <:< typeOf[String] => println("string: " + c.value.toUpperCase)
  case c: Container[Double] if typeOf[A] <:< typeOf[Double] => println("double: " + math.sqrt(c.value))
  case c: Container[_] => println("other")
}

0
0 Comments

问题的原因是在Scala中,当我们需要对泛型类型进行模式匹配时,不能直接使用类型参数,而是需要使用类型擦除后的类型进行匹配。这是因为在运行时,泛型类型的类型参数信息会被擦除,只剩下原始类型。

解决方法是通过使用类型擦除后的类型进行匹配。在给定的示例中,我们可以通过匹配容器中的值的类型来实现。由于容器只包含一个泛型类型的值,我们可以直接匹配该值的类型。

具体的解决方法如下所示:

container match {
  case Container(x: String) => println("string")
  case Container(x: Double) => println("double")
  case _ => println("w00t")
}

在这个例子中,我们使用了模式匹配的语法来匹配容器的值的类型。首先,我们匹配了包含字符串的情况,并打印出"string"。然后,我们匹配了包含双精度浮点数的情况,并打印出"double"。最后,我们使用通配符来匹配其他情况,并打印出"w00t"。

这样,我们就可以根据容器中值的类型进行相应的操作。通过使用类型擦除后的类型进行模式匹配,我们可以绕过泛型类型在运行时的类型参数擦除问题,实现对泛型类型的模式匹配。

0