【问题标题】:Scala generic cast from String function来自 String 函数的 Scala 泛型转换
【发布时间】:2015-06-03 18:09:38
【问题描述】:

如何编写函数将字符串转换为泛型类型(Double、Int、Float 等)?

这是我正在寻找的功能的伪代码:

def castFromString[A: Manifest](value: String): A = {
  if (A == Double) {
    parseDouble(value)
  } else if (A == Int) {
    parseInt(value)
  } else {
    value.toString()
  }
}

【问题讨论】:

  • 我认为你的意思是A: ClassTag/A: TypeTagManifests 有点过时了。检查this guide

标签: scala generics casting


【解决方案1】:

我认为没有办法使用ManifestClassTagTypeTag 来编写函数,因为它们只能在运行时告诉您类型。要使伪代码函数之类的东西起作用,必须在编译时知道返回类型。

但是,可以通过定义type class 并为您希望从中转换的类型编写实例来实现该功能。这是一个例子:

import java.lang.Double.parseDouble
import java.lang.Integer.parseInt

trait CastableFromString[A] {
  def fromString(string: String): A
}

object CastableFromString {
  implicit object DoubleCastableFromString extends CastableFromString[Double] {
    override def fromString(string: String): Double =
      parseDouble(string)
  }

  implicit object IntCastableFromString extends CastableFromString[Int] {
    override def fromString(string: String): Int =
      parseInt(string)
  }

  implicit object IntListCastableFromString extends CastableFromString[List[Int]] {
    override def fromString(string: String): List[Int] =
      string.split(',').map(parseInt).toList
  }

  def castFromString[A](string: String)(implicit cast: CastableFromString[A]): A =
    cast.fromString(string)
}

object Main {
  import CastableFromString._

  def main(args: Array[String]): Unit = {
    val d = castFromString[Double]("4.5")
    val i = castFromString[Int]("42")
    val li = castFromString[List[Int]]("1,2,3")
    println(s"d=$d i=$i li=$li")
  }
}

或者,为了更接近类似于您的伪代码的内容,您可以使用macro。这是一个示例(不适用于 Scala 2.11 之前的任何版本):

import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context

class StringCasterImpl(val c: Context) {
  def castFromStringImpl[A: c.WeakTypeTag](string: c.Expr[String]): c.Tree = {
    import c.universe._
    val t = c.weakTypeOf[A]
    if (t =:= typeOf[Double]) {
      q"java.lang.Double.parseDouble($string)"
    } else if (t =:= typeOf[Int]) {
      q"java.lang.Integer.parseInt($string)"
    } else if (t =:= typeOf[List[Int]]) {
      q"$string.split(',').map(java.lang.Integer.parseInt).toList"
    } else {
      c.abort(c.enclosingPosition, s"Don't know how to cast $t to String")
    }
  }
}

object StringCaster {
  def castFromString[A](string: String): A =
    macro StringCasterImpl.castFromStringImpl[A]
}

// note that this object must be in a separate compilation unit
object Main {
  import StringCaster._

  def main(args: Array[String]): Unit = {
    val d = castFromString[Double]("4.2")
    val i = castFromString[Int]("42")
    val li = castFromString[List[Int]]("1,2,3")
    println(s"d=$d i=$i li=$li")
  }
}

【讨论】:

  • 我喜欢 scala,但这只是其中的几个“疯狂的破窗”之一,其中 java 用一个单词解决方案做得更好(而不是需要对象/类的层次结构)。跨度>
【解决方案2】:

您可以定义将字符串转换为特定类型的隐式函数。

然后在需要时会自动施放。

math.max(1,"3") // throws error

但是现在

implicit def StringToInt(x: String): Int = x

math.max(1,"3")  //>> 3

【讨论】:

  • 这种程度的隐含是非常危险的恕我直言。
【解决方案3】:

如果你想完全这样做,你可以匹配ClassTag中的runtimeClass

def castFromString[A: ClassTag](value: String): A = {
  val c = implicitly[ClassTag[A]].runtimeClass
  if(c == classOf[Double]) parseDouble(value).asInstanceOf[A]
  else ...
}

注意需要asInstanceOf 演员表。 @Olli 建议的类型类方法更安全、更惯用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-16
    • 2011-09-12
    • 1970-01-01
    • 1970-01-01
    • 2021-02-21
    • 1970-01-01
    相关资源
    最近更新 更多