15

假设我有这门课

case class Test (id: Long, name: String)

和这个类的一个实例:

Test :
id -> 1
name -> toto

我想创建一个 Map[String, String] 如下:

Map( "id" -> "1", "name" -> "toto")

我的问题是:有没有办法把这个 Test 实例变成 Map[String, String] ?我想避免使用这种方法:

def createMap(instance: Test): Map[String, String] = {
    val map = new Map[String, String]
    map.put("id", instance.id.toString)
    map.put("name", instance.name)
    map
}

如果在 Scala 中没有这样做的方法,有没有办法迭代类属性?也许我可以创建一个通用函数来这样做:

def createMap(instance: T): Map[String, String] = {
   val map = new Map[String, String]
   //pseudocode 
   for  ((name, value) <- instance.getClassProperties.getValues) {
      case value.isInstanceOf[String] : map.push(name, value)
      case _ : map.push(name, value.toString)
    }
    map
}

那可能吗 ?如果您有很好的示例/链接,我很感兴趣。

4

3 回答 3

21

是的,这是可能的。从 Scala 2.10 开始,您可以使用反射。

假设你有:

val test = Test(23423, "sdlkfjlsdk")

以下将为您提供您想要的:

import reflect.runtime.universe._
import reflect.runtime.currentMirror

val r = currentMirror.reflect(test)
r.symbol.typeSignature.members.toStream
  .collect{case s : TermSymbol if !s.isMethod => r.reflectField(s)}
  .map(r => r.symbol.name.toString.trim -> r.get.toString)
  .toMap

简单地迭代案例类的字段值,.productIterator在其实例上使用。

于 2012-10-09T10:35:20.607 回答
11

您正在处理的主题在 StackOverFlow 上变得令人难以置信地反复出现,如果您想要一个类型安全的实现,问题就不是微不足道的。

解决问题的一种方法是使用反射(如建议的那样),但我个人更喜欢使用类型系统和隐式。

有一个著名的库,由一个非常聪明的人开发,它可以让您执行高级操作,例如将任何案例类转换为类型安全的异构列表,或创建异构映射,可用于实现“可扩展记录”。该库称为 Shapeless,这里有一个示例:

object RecordExamples extends App {
  import shapeless._
  import HList._
  import Record._

  object author  extends Field[String]  { override def toString = "Author" }
  object title   extends Field[String]  { override def toString = "Title" }
  object id      extends Field[Int]     { override def toString = "ID" }
  object price   extends Field[Double]  { override def toString = "Price" }
  object inPrint extends Field[Boolean] { override def toString = "In print" }

  def printBook[B <: HList](b : B)(implicit tl : ToList[B, (Field[_], Any)]) = {
    b.toList foreach { case (field, value) => println(field+": "+value) }
    println
  }

  val book =
    (author -> "Benjamin Pierce") ::
    (title  -> "Types and Programming Languages") ::
    (id     ->  262162091) ::
    (price  ->  44.11) ::
    HNil

  printBook(book)

  // Read price field
  val currentPrice = book.get(price)  // Static type is Double
  println("Current price is "+currentPrice)
  println

  // Update price field, relying on static type of currentPrice
  val updated = book + (price -> (currentPrice+2.0))
  printBook(updated)

  // Add a new field
  val extended = updated + (inPrint -> true)
  printBook(extended)

  // Remove a field
  val noId = extended - id 
  printBook(noId)
}

Book 的行为类似于类型安全映射,可以使用对象作为键进行索引。如果您有兴趣了解更多信息,那么这篇文章可能是一个很好的切入点:

HLists 只不过是一种复杂的元组编写方式吗?

于 2012-10-09T10:13:48.627 回答
0

开始Scala 2.13, case classes(作为 的实现Product)提供了一个productElementNames方法,该方法在其字段名称上返回一个迭代器。

通过使用获得的字段值压缩字段名称,productIterator我们通常可以获得关联Map[String, Any],并通过将值映射toString到关联Map[String, String]

// case class Test(id: Long, name: String)
// val x = Test(1, "todo")
(x.productElementNames zip x.productIterator.map(_.toString)).toMap
// Map[String,String] = Map("id" -> "1", "name" -> "todo")
于 2019-03-10T13:56:55.643 回答