将字符串转换为通用类型的Scala通用函数

时间:2019-02-04 13:46:48

标签: scala generics

我有一个函数,应该用它的值查找命令行参数,然后将其返回转换为类型P

def parameter[P](name: String)(implicit tag: ClassTag[P]): P = {

    val paramName = s"--$name"

    args.sliding(2, 2).toList.collectFirst {
      case Array(`paramName`, param: String) => {
        // if P is Int => param.toInt
        // if P is Double => param.toDouble
      }
    }.get
  }

我该怎么做?我发现ClassTag是一种解决方法,但是在这种情况下却无法弄清楚如何使用它。

3 个答案:

答案 0 :(得分:5)

您可以将class标签与要匹配的类型的标签进行比较:

import scala.reflect.{ClassTag, classTag}

def parameter[P](args: Array[String], name: String)(implicit tag: ClassTag[P]): P = {
  val paramName = s"--$name"

  args.sliding(2, 2).toList.collectFirst {
    case Array(`paramName`, param: String) => (
      if (tag == classTag[Double]) {
        param.toDouble
      } else if (tag == classTag[Int]) {
        param.toInt
      } // and so on...
    ).asInstanceOf[P]
  }.get
}

您当然也可以使用模式匹配。它按预期工作:

scala> parameter[Int](Array("--foo", "123"), "foo")
res0: Int = 123

scala> parameter[Double](Array("--foo", "123"), "foo")
res1: Double = 123.0

但是,这种方法有很多缺点-您必须了解parameter的定义中要解析的所有类型,等等-最好使用合适的类型类专为您正在执行的解析而设计。

答案 1 :(得分:1)

很少有资源可以帮助您:

链接之一中的示例:

scala>   val StringClass = classTag[String]
scala>   val IntClass = classTag[Int]
scala>   def typeList[T](list: List[T])(implicit tag: ClassTag[T]) =
          tag match {
            case StringClass => "It's a String!"
            case IntClass => "It's an Integer."
            case _ => "It's something else entirely"
          }

答案 2 :(得分:1)

所以Travis'ColOfAbRiX's的答案是可行的解决方案。但是正如Travis指出的那样,它们不是 typesafe

这是我最终得到的解决方案(请参见Scala Type Classes 101: Introduction

为所需类型定义隐式转换器

trait StringConverter[P] {
  def convert(a: String): P
}

implicit val string2string = new StringConverter[String] {
  def convert(a: String): String = a
}
implicit val string2double = new StringConverter[Double] {
  def convert(a: String): Double = a.toDouble
}
implicit val string2int = new StringConverter[Int] {
  def convert(a: String): Int = a.toInt
}
implicit val string2long = new StringConverter[Long] {
  def convert(a: String): Long = a.toLong
}
implicit val string2bool = new StringConverter[Boolean] {
  def convert(a: String): Boolean = a.toBoolean
}

然后按如下所示使用它们:

def parameter[P](name: String)(implicit converter: StringConverter[P]): P = {
  val paramName = s"--$name"

  val res = args.sliding(2, 2).toList.collectFirst {
    case Array(`paramName`, param: String) => converter.convert(param)
  }

  res.get
}

按原样接受(IMHO)更加干净和类型安全-如果您未为使用的一种或多种类型定义转换,则它将无法编译(ClassTag解决方案将在运行时编译并失败)。

评论,更正,建议非常感激。