包含混合类型值的Scala映射
我有一个返回的函数(groovy代码) 在scala中,我在上面更改为scala映射,但随后我被迫将其声明为包含混合类型值的Scala映射,scala,Scala,我有一个返回的函数(groovy代码) 在scala中,我在上面更改为scala映射,但随后我被迫将其声明为 Map[String, Any] 但这样做的缺点是,如果我访问一个密钥,例如 地图(“文字”) 我必须加上样板 map2("words").asInstanceOf[String] scala中有没有更好的方法不需要我添加instanceof?为了避免强制转换并从静态键入中获益,您可以返回元组(String,Int,Int): 或者对结果使用case类: case class MyR
Map[String, Any]
但这样做的缺点是,如果我访问一个密钥,例如
地图(“文字”)
我必须加上样板
map2("words").asInstanceOf[String]
scala中有没有更好的方法不需要我添加instanceof?为了避免强制转换并从静态键入中获益,您可以返回元组
(String,Int,Int)
:
或者对结果使用case类:
case class MyResult(line: String, row: Int, col: Int)
def getResult = MyResult("one two", 23, 45)
val res = getResult
res.line // the line
// alternatively use the extractor provided by the case class
val MyResult(line, row, _) = getResult // col is discarded
line // the line
row // the row
我更喜欢case类,因为字段是命名的,它实际上只多了一行。编辑:我知道您得到的映射是原样的,需要一种更干净的接口。当然,如果适用的话,案例类方法要优于我下面提出的方法
如果键始终映射到相同的值类型,则可以执行以下操作:
class TypedKey[T] {
def name = {
// assumes you declare only `object` instances
val simpleName = getClass.getSimpleName
val moduleName = if (simpleName.endsWith("$")) simpleName.substring(0, simpleName.size - 1) else simpleName
val lastDollar = moduleName.lastIndexOf('$')
if (lastDollar == -1) moduleName else moduleName.substring(lastDollar + 1)
}
}
object RubyKeys {
object words extends TypedKey[String]
object row extends TypedKey[Int]
object col extends TypedKey[Int]
}
class MapWrapper(val underlying: Map[String, Any]) {
def apply[T](key: TypedKey[T]) = underlying(key.name).asInstanceOf[T]
}
def main(args: Array[String]) {
val map = Map("words" -> "one two", "row" -> 23, "col" -> 45)
val wrapper = new MapWrapper(map)
import RubyKeys._
val w = wrapper(words) // String
val r = wrapper(row) // Int
val c = wrapper(col) // Int
println(w, r, c)
}
在您的示例中,只有两种可能的值类型将允许将
类型与子类型Left
和Right
一起使用:
val m = Map("words" -> Left("one two"), "rows"-> Right(23), "cols"-> Right(45))
如果您从地图中获取一个值,您可以检查您拥有的内容,例如使用模式匹配或使用isLeft
和isRight
,并相应地“展开” 在这里,案例类是您的朋友。如果映射的键是case类,则可以强制客户端代码正确处理类型(并强制它正确处理所有类型)
S:\>scala
欢迎使用Scala版本2.9.0.final(Java热点(TM)64位服务器虚拟机,Java 1.6.025)。
键入要计算的表达式。
键入:有关详细信息的帮助。
scala>密封抽象类东西;
定义类事物
scala>case类烤面包机(slices:Int)扩展了东西;
限定等级烤面包机
scala>case类Bucket(内容:String)扩展了东西;
定义类存储桶
scala>val things=Map(“烤面包店”->烤面包机(2),“巴克”->桶(“东西”);
things:scala.collection.immutable.Map[java.lang.String,可以用Thing序列化的产品]=Map(toasty->Toaster(2),buck->Bucket(stu
(ff))
scala>for(x println(k+“”+s)
案例(k,Bucket(c))=>println(k+“”+c)
}
烤面包2
雄鹿的东西
这里的关键是match语句正在分解各种大小写,并为您提供正确类型的变量以匹配其中的字段。通过声明抽象类为密封类,您可以让编译器知道它拥有所有可用的子类。有了这些信息,它可以告诉您何时缺少大小写,它还可以做进一步的优化
class TypedKey[T] {
def name = {
// assumes you declare only `object` instances
val simpleName = getClass.getSimpleName
val moduleName = if (simpleName.endsWith("$")) simpleName.substring(0, simpleName.size - 1) else simpleName
val lastDollar = moduleName.lastIndexOf('$')
if (lastDollar == -1) moduleName else moduleName.substring(lastDollar + 1)
}
}
object RubyKeys {
object words extends TypedKey[String]
object row extends TypedKey[Int]
object col extends TypedKey[Int]
}
class MapWrapper(val underlying: Map[String, Any]) {
def apply[T](key: TypedKey[T]) = underlying(key.name).asInstanceOf[T]
}
def main(args: Array[String]) {
val map = Map("words" -> "one two", "row" -> 23, "col" -> 45)
val wrapper = new MapWrapper(map)
import RubyKeys._
val w = wrapper(words) // String
val r = wrapper(row) // Int
val c = wrapper(col) // Int
println(w, r, c)
}
val m = Map("words" -> Left("one two"), "rows"-> Right(23), "cols"-> Right(45))
S:\>scala
Welcome to Scala version 2.9.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_25).
Type in expressions to have them evaluated.
Type :help for more information.
scala> sealed abstract class Thing;
defined class Thing
scala> case class Toaster(slices: Int) extends Thing;
defined class Toaster
scala> case class Bucket(contents: String) extends Thing;
defined class Bucket
scala> val things = Map("toasty" -> Toaster(2), "buck" -> Bucket("stuff"));
things: scala.collection.immutable.Map[java.lang.String,Product with Serializable with Thing] = Map(toasty -> Toaster(2), buck -> Bucket(stu
ff))
scala> for (x <- things) x match {
case (k,Toaster(s)) => println(k + " " + s)
case (k,Bucket(c)) => println(k + " " + c)
}
toasty 2
buck stuff