【问题标题】:What is the best way to create and pass around dictionaries containing multiple types in scala?在scala中创建和传递包含多种类型的字典的最佳方法是什么?
【发布时间】:2011-08-02 20:47:28
【问题描述】:

字典是指从名称到值的轻量级映射,可用作方法的返回值。

我知道的选项包括创建案例类、创建匿名对象以及从 Strings -> Any 创建映射。

  • 案例类需要脑力开销才能创建(名称),但是是强类型的。
  • 匿名对象似乎没有很好的文档记录,我不清楚如何将它们用作参数,因为没有命名类型。
  • 来自字符串的映射 -> 任何需要转换才能检索。

还有什么更好的吗?

理想情况下,这些可以从 json 构建并在适当的时候转换回它。

我不需要静态类型(虽然这很好,但我知道这是不可能的) - 但我确实想避免显式转换。

【问题讨论】:

  • 你能添加一个你想要使用的例子吗?
  • 什么叫“轻量级地图”? scala.collection.immutable.Map 是重量级人物吗?
  • 如果像 that answer 这样的东西适合你,我可以重写它以适应你的问题。

标签: scala


【解决方案1】:

这是你想要的基本问题:

def get(key: String): Option[T] = ...

val r = map.get("key")

r 的类型将从get 的返回类型定义——那么,该类型应该是什么?从哪里可以定义?如果将其设为类型参数,则相对简单:

import scala.collection.mutable.{Map => MMap}
val map: MMap[String, (Manifest[_], Any) = MMap.empty
def get[T : Manifest](key: String): Option[T] = map.get(key).filter(_._1 <:< manifest[T]).map(_._2.asInstanceOf[T])
def put[T : Manifest](key: String, obj: T) = map(key) = manifest[T] -> obj

例子:

scala> put("abc", 2)

scala> put("def", true)

scala> get[Boolean]("abc")
res2: Option[Boolean] = None

scala> get[Int]("abc")
res3: Option[Int] = Some(2)

当然,问题在于您必须告诉编译器您希望在该键下的映射中存储什么类型。不幸的是,根本没有办法解决这个问题:编译器在编译时无法知道该键下将存储什么类型。

你采取的任何解决方案都会遇到同样的问题:不知何故,你必须告诉编译器应该返回什么类型。

现在,这不应该成为 Scala 程序的负担。拿上面的r...然后你会用那个r做某事,对吧?你使用它的东西会有适合某种类型的方法,既然你知道这些方法是什么,那么你也必须知道r的类型必须是什么。

如果不是这种情况,那么代码就存在根本性的问题 - 或者,您可能还没有从想要地图到知道如何使用它。

【讨论】:

  • 太酷了!但我的理想是我可以val b: Boolean = get("abc") - 所以我不需要在 get 调用中明确指定类型或强制转换。理想情况下,如果方法foo 只接受布尔值,那么foo(get("abc")) 也可以工作,因为类型系统应该知道我可以得到一个布尔值。如果foo 的参数类型不明确,那么我可以指定显式类型。
  • @wn- 这行不通,因为T 可以而且将会被推断为Nothing——毕竟Nothing 是所有事物的子类型,所以val b: Boolean = get[Nothing]("abc") 是完全有效。如果您向 get 添加一个隐式参数,那么您可能会得到它的工作,而不是除 Nothing 之外的任何类型都可以满足 - 最近有一个关于此的问题。
【解决方案2】:

所以您想解析 json 并将其转换为类似于 json 输入中描述的 javascript 对象的对象?如果你想要静态类型,case 类几乎是你唯一的选择,并且已经有库处理这个,例如 lift-json。

另一种选择是使用 Scala 2.9 对动态类型的实验性支持。这将以牺牲类型安全为代价为您提供优雅的语法。

【讨论】:

    【解决方案3】:

    您可以使用我在the casbah library 中看到的方法,当您将类型参数显式传递给 get 方法并将实际值强制转换到 get 方法中时。这是一个简单的例子:

    case class MultiTypeDictionary(m: Map[String, Any]) {
      def getAs[T <: Any](k: String)(implicit mf: Manifest[T]): T =
        cast(m.get(k).getOrElse {throw new IllegalArgumentException})(mf)
    private def cast[T <: Any : Manifest](a: Any): T = a.asInstanceOf[T] }
    implicit def map2multiTypeDictionary(m: Map[String, Any]) = MultiTypeDictionary(m)
    val dict: MultiTypeDictionary = Map("1" -> 1, "2" -> 2.0, "3" -> "3")
    val a: Int = dict.getAs("1") val b: Int = dict.getAs("2") //ClassCastException val b: Int = dict.getAs("4") //IllegalArgumetExcepton

    你应该注意没有真正的编译时检查,所以你必须处理所有异常的缺点。

    UPD 工作 MultiTypeDictionary 类

    【讨论】:

    • 这正是我想要的——但是val a... 在repl 上给出了error: polymorphic expression cannot be instantiated to expected type;
    【解决方案4】:

    如果您只有有限数量的可以作为值出现的类型,您可以使用某种联合类型(也称为不相交类型),例如Map[Foo, Bar | Baz | Buz | Blargh]。如果你只有两种可能,你可以使用Either[A,B],给你一个Map[Foo, Either[Bar, Baz]]。对于三种类型,您可能会作弊并使用Map[Foo, Either[Bar, Either[Baz,Buz]]],但这种语法显然不能很好地扩展。如果您有更多类型,您可以使用...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-24
      • 2018-02-07
      • 2010-09-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-04
      • 1970-01-01
      相关资源
      最近更新 更多