如何获取地图默认值(如果存在)

How to get Map default value if it exist

我想检索地图的默认值(如果存在)。这是我尝试过的:

def defaultValue[A, B](m: Map[A, B]): Option[B] = {
  m match {
    case m: Map.WithDefault[A, B] => Some(m.default(null))
    case _ => None
  }
}

这不起作用,因为 null 不是 A 的子类型。如何检索具有常量函数的 Map.WithDefault 的默认值?

我试过的

生成类型为 A 的随机值?默认值是使用常量函数编码的,这就是为什么它需要一个参数,但在我的例子中,不应该使用参数。此外,当我输入类似 ??? 类型 Nothing 的内容时,它会在尝试 return 常量值之前评估参数,从而引发异常。

我的问题与 this question 有关,但他们没有提供直接检索默认值的方法。

我想你可以通过在任意键上调用 #default 来实现:

val map = Map(1 -> "a").withDefaultValue("b")

map(1)         // yields "a"
map.default(1) // yields "b"

虽然不是很好,但如果没有默认值会抛出异常,所以你需要用Try包裹起来,然后使用模式匹配来提取它。

Try(map.default(1)).toOption match {
  case Some(value) => // do something with the value
  case _           => // do something else
}

您可以添加一个下限,将您的方法签名重写为:

def defaultValue[A >: Null <: AnyRef, B](m: Map[A, B]): Option[B]

或者您可以使用 null.asInstanceOf[A]:

null 转换为 A
case m: Map.WithDefault[A, B] => Some(m.default(null.asInstanceOf[A]))

并不是说此版本也适用于 A <: AnyVal(请记住,例如 null.asInstanceOf[Int] == 0)。


仅需注意一点:您可以匹配 case _: Map.WithDefault[_, _],因为无法在 运行 时检查此类型测试中的外部引用。

您可以使用类型 class 模式来处理您的问题。您会收到错误消息,因为 A 可以是 AnyVal class,如 Int、Float 等。但您可以简单地克服此限制,请考虑以下事项:

trait DummyProvider[T] {
  def dummy: T
}

implicit def anyRefDummy[T >: Null] = new DummyProvider[T] {
  def dummy: T = null
}

implicit def numericDummy[T](implicit ev: Numeric[T]) = new DummyProvider[T] {
  def dummy: T = ev.zero
}
//probably needs to add instance for boolean

现在您需要在代码中做的就是这样:

def defaultValue[A, B](m: Map[A, B])(implicit ev: DummyProvider[A]): Option[B] = {
  m match {
    case m: Map.WithDefault[A, B] => Some(m.default(ev.dummy))
    case _ => None
  }
}

这是不可能的

一般来说这是不可能的,因为默认的“值”实际上是一个函数。所以一般情况下没有单一的默认值。

以下来源来自 Scala 2.12.*.

scala.collection.immutable.Map中存在以下内容:

  /** The same map with a given default function.
   *  Note: `get`, `contains`, `iterator`, `keys`, etc are not affected by `withDefault`.
   *
   *  Invoking transformer methods (e.g. `map`) will not preserve the default value.
   *
   *  @param d     the function mapping keys to values, used for non-present keys
   *  @return      a wrapper of the map with a default value
   */
def withDefault[V1 >: V](d: K => V1): immutable.Map[K, V1] = new Map.WithDefault[K, V1](this, d)

这是来自scala.collection.Map:

  /** An abstract shell used by { mutable, immutable }.Map but not by collection.Map
   *  because of variance issues.
   */
  abstract class WithDefault[K, +V](underlying: Map[K, V], d: K => V) extends AbstractMap[K, V] with Map[K, V] with Serializable {
    override def size               = underlying.size
    def get(key: K)                 = underlying.get(key) // removed in 2.9: orElse Some(default(key))
    def iterator                    = underlying.iterator
    override def default(key: K): V = d(key)
}