提取值时隐式转换任意Map到Map的最佳方法

时间:2014-10-25 07:33:35

标签: scala

当我使用布尔值和字符串创建混合Map时:

scala> val map = Map('boolean -> true, 'string -> "string")
map: scala.collection.immutable.Map[Symbol,Any] = Map('boolean -> true, 'string -> string)

我尝试直接访问布尔部分,我得到:

scala> if (map('boolean)) true else false
<console>:9: error: type mismatch;
 found   : Any
 required: Boolean
              if (map('boolean)) true else false
                     ^

所以我必须定义一个隐含的来使它工作:

scala> implicit def anyAsBoolean(x: Any) = x.asInstanceOf[Boolean]
warning: there were 1 feature warning(s); re-run with -feature for details
anyAsBoolean: (x: Any)Boolean

scala> if (map('boolean)) true else false
res3: Boolean = true

有没有办法让这个Map做隐式转换而不必在客户端代码中添加隐式部分?

3 个答案:

答案 0 :(得分:4)

您可以尝试Shapeless HMap

class BiMapIS[K, V]
implicit val intToString = new BiMapIS[Int, String]
implicit val stringToInt = new BiMapIS[String, Int]

val hm = HMap[BiMapIS](23 -> "foo", "bar" -> 13)
//val hm2 = HMap[BiMapIS](23 -> "foo", 23 -> 13)   // Does not compile - strong type

scala> hm.get(23)
res0: Option[String] = Some(foo)

scala> hm.get("bar")
res1: Option[Int] = Some(13)

因此,客户端不需要显式地转换Map或编写任何隐式转换(intToString,stringToInt仅用于映射定义)。请注意,HMap的类型将与{Int -> String, String -> Int}绑定,您也无法执行{String -> A; String -> B},因此如果可能,应将字符串替换为某些案例对象。

答案 1 :(得分:3)

为什么不把它直接放在那里:

map('boolean).asInstanceOf[Boolean]

或者您可以使用Either来保留类型信息:

val map: Map[Symbol, Either[Any, Boolean]] = Map(
  'boolean -> Right(true),
  'string  -> Left("string"))

map('boolean).fold(throw new IllegalStateException(_), b => b)

使用Scalaz:

val map: Map[Symbol, Either[Any, Boolean]] = Map(
  'boolean -> true.right[Any], 
  'string  -> "string".left[Boolean])

map 'boolean | throw new IllegalStateException

答案 2 :(得分:0)

所以我最终从Map[Symbol, Any]继承并以这种方式进行提取:

scala> :paste
// Entering paste mode (ctrl-D to finish)

object OptionMap {
  def apply(options_map: Map[Symbol, Any]) = new OptionMap(options_map)

  def apply(kv: (Symbol, Any)*) = new OptionMap(kv.toMap)
}

class OptionMap(options_map: Map[Symbol, Any]) extends Map[Symbol, Any] {

  def apply[T](name: Symbol) : T = options_map(name).asInstanceOf[T]

  def get[T](name: Symbol) : T = options_map(name).asInstanceOf[T]

  def get(name: Symbol) : Option[Any] = options_map.get(name)

  override def contains(name: Symbol): Boolean = options_map.contains(name)

  def +[B1 >: Any](kv: (Symbol, B1)): OptionMap = OptionMap(options_map + kv)

  def -(key: Symbol): OptionMap = OptionMap(options_map - key)

  def iterator = options_map.iterator
}

// Exiting paste mode, now interpreting.

defined module OptionMap
defined class OptionMap

scala> val omap = OptionMap('boolean -> true, 'string -> "string", 'int -> 5, 'double -> 3.14)
omap: OptionMap = Map('boolean -> true, 'string -> string, 'int -> 5, 'double -> 3.14)

scala> if(omap.contains('boolean) && omap('boolean)) true else false
res0: Boolean = true

scala> omap[String]('string) + " world!"
res1: String = string world!

scala> omap[Int]('int) + 3
res2: Int = 8

scala> omap[Double]('double) + 3
res3: Double = 6.140000000000001

scala> omap + ('extra -> true)
res4: OptionMap = Map('string -> string, 'double -> 3.14, 'boolean -> true, 'int -> 5, 'extra -> true)

scala> omap - 'extra
res5: OptionMap = Map('boolean -> true, 'string -> string, 'int -> 5, 'double -> 3.14)

所以它现在做我想要的,客户端代码不需要有任何暗示。当然,客户端需要知道每个key -> value对中存储的类型,但这应该是。

相关问题