在映射上的模式匹配

时间:2014-07-19 02:38:23

标签: scala playframework-2.0

出于示例目的,假设您有以下内容:

import play.api.data._
import play.api.data.Forms._
case class TestDomainClass(text: String, number: Int, date: DateTime, long: Long, boolean: Boolean)
val testForm = Form(
  mapping(
    "textField" -> text,
    "numberField" -> number,
    "dateField" -> jodaDate,
    "longField" -> longNumber,
    "booleanField" -> boolean
  )(TestDomainClass.apply)(TestDomainClass.unapply)
)

我的目标是在每个字段的参数类型上进行模式匹配。例如:对于textField,参数类型为String,而dateField则为org.joda.time.DateTime


第一次尝试解决方案

def typeOfMappingUsingTypeTag[T: TypeTag](m: Mapping[T]) = typeOf[T] match {
  case t if t =:= typeOf[String] => println("type is String")
  case t if t =:= typeOf[Int] => println("type is Int")
  case t if t =:= typeOf[org.joda.time.DateTime] => println("type is DateTime")
  case t if t =:= typeOf[Long] => println("type is Long")
  case t if t =:= typeOf[Boolean] => println("type is boolean")
  case _ => println("type is something else")
}
for(m <- testForm.mapping.mappings if m.key.nonEmpty) {
  print(m.key)
  typeOfMappingUsingTypeTag(m)
}

使用以下输出:

textFieldtype is something else
numberFieldtype is something else
dateFieldtype is something else
longFieldtype is something else
booleanFieldtype is something else

所以没有运气,这在某种程度上是预期的,因为在source the Mapping中没有与TypeTag绑定的上下文。


第二次尝试解决方案

def typeOfMappingUsingPatternMatch[T](m: Mapping[T]) = m match {
  case text => println("type is String")
  case number => println("type is Int")
  case jodaTime => println("type is DateTime")
  case longNumber => println("type is Long")
  case boolean => println("type is Boolean")
  case _ => println("type is something else")
}
for(m <- testForm.mapping.mappings if m.key.nonEmpty) {
  print(m.key)
  typeOfMappingUsingPatternMatch(m)
}

使用以下输出:

textFieldtype is String
numberFieldtype is String
dateFieldtype is String
longFieldtype is String
booleanFieldtype is String

以下编译警告:

[warn] /aFolder/aProject/app/controllers/IPBlocks.scala:58: patterns after a variable pattern cannot match (SLS 8.1.1)
[warn]     case text => println("type is String")
[warn]          ^
[warn] /aFolder/aProject/app/controllers/IPBlocks.scala:59: unreachable code due to variable pattern 'text' on line 58
[warn]     case number => println("type is Int")
[warn]                           ^

与其余案件的警告相同。

如果我改变了案例的顺序,无论哪种情况最终成为第一个,都是被打印的那个(例如,如果案例布尔是第一种情况,那么每个案例的输出将是is Boolean字段)


第三次尝试解决方案

考虑输出:

val t = text
println("t == number " + (t == number))
println("t == text " + (t == text))
println("t eq number " + (t eq number))
println("t eq text " + (t eq text))

t == number true
t == text true
t eq number false
t eq text true

使用ifs和eq似乎可以解决问题,所以我尝试过:

def typeOfMappingUsingIfAndEq[T](m: Mapping[T]) = {
  if (m eq text) {
    println("type is String")
  } else if (m eq number) {
    println("type is Int")
  } else if (m eq jodaDate) {
    println("type is DateTime")
  } else if (m eq longNumber) {
    println("type is Long")
  } else if (m eq boolean) {
    println("type is Boolean")
  } else {
    println("type is something else")
  }
}
for(m <- testeForm.mapping.mappings if m.key.nonEmpty) {
  print(m.key)
  typeOfMappingUsingIfAndEq(m)
}

使用以下输出

textFieldtype is something else
numberFieldtype is something else
dateFieldtype is something else
longFieldtype is something else
booleanFieldtype is something else

那么,问题是否有解决方案?如果是,那是什么? 另外,为什么地狱不能解决第三种解决方案呢?

1 个答案:

答案 0 :(得分:0)

我不确定我是否完全理解你的目标,但是如果你想根据对象类型做事,你可以用这种方式匹配模式:

value match {
    case stringValue : String => // value es a String! You can use the stringValue variable which's type is String but reference the same object
    case intValue : Int => // value is an Int. Do stuff.
    ...
    case _ => // any other case
}

关于你的第三个实现,请注意使用eq。 Eq检查身份,这与平等不同。 (您可以查看this post了解更多信息)。另外,由于您的代码脱离了上下文,我不确定这些变量是如何定义的,但是,例如,如果我们采用这些行:

def typeOfMappingUsingIfAndEq[T](m: Mapping[T]) = {
    if (m eq text) {
    ...

如果文字变量的类型为字符串(因为它位于 TestDomainClass ),则无法进行等于/等于m,其类型为 Mapping [T] )。

希望这有帮助!

祝你好运!