2012-10-09 13 views
11

Powiedzmy, że mam tę klasęW scala, jak zmienić wartości obiektu w Map [String, String]?

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

i instancję tej klasy:

Test : 
id -> 1 
name -> toto 

Chciałbym stworzyć mapę [String, String] następujące:

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

Moje pytanie brzmi: czy istnieje sposób na przekształcenie tego wystąpienia testu w mapę [łańcuch, ciąg]? Chcę uniknąć stosowanie metody jak ten:

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 
} 

Jeśli nie ma sposobu, aby to zrobić w Scala, czy istnieje sposób iteracyjne nad właściwości klasy? Może mogę utworzyć ogólną funkcję, aby to zrobić:

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 
} 

Czy to możliwe? Jeśli masz dobre przykłady/linki, jestem zainteresowany.

+0

Odpowiedzi na [to pytanie] (http://stackoverflow.com/questions/2224251/reflection-on-a-scala-case-class) mogą pomóc. – incrop

+0

Nie żebym nie lubił ogólnych rozwiązań, ale dla tego problemu użyłbym 'Map (" id "-> t.id.toString," name "-> t.name)' gdzie 't' jest instancją 'Testuj". – sschaef

+0

Dziękujemy za napiwek! – alexgindre

Odpowiedz

19

Tak, to możliwe. Od Scala 2.10 można używać refleksji.

zakładając, że masz:

val test = Test(23423, "sdlkfjlsdk") 

Poniższa będzie Ci to, co chcesz:

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 

po prostu iteracyjne nad wartości pól klasy przypadek użycia .productIterator na jego wystąpienie.

+0

Czy ten proces może być odwrotny? Od mapy do obiektu? Jeśli imię i nazwisko w klasie podano –

+1

@JeffLee Tak. Jest to możliwe zarówno w przypadku refleksji, jak i makr. Oto [przykład z wykorzystaniem odbicia Scala 2.10] (https://github.com/sorm/sorm/blob/master/src/main/scala/sorm/reflection/Reflection.scala#L50-L58). –

10

Temat, z którym mamy do czynienia, staje się niewiarygodnie powtarzający się na StackOverFlow, a problem nie jest trywialny, jeśli chcemy mieć implementację typesafe.

Jednym ze sposobów rozwiązania problemu jest użycie odbicia (zgodnie z sugestią), ale ja osobiście wolę używać systemu typu i implicite.

Istnieje dobrze znana biblioteka, opracowana przez wyjątkowo inteligentnego faceta, która pozwala wykonywać zaawansowane operacje, takie jak przekształcenie dowolnej klasy przypadków w heterogeniczną listę bezpiecznych typów lub tworzenie heteregeneous map, które można wykorzystać do implementacji "extensible" dokumentacja". Biblioteka nazywa Shapeless, a tu jeden przykład:

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 zachowuje się jak mapa typesafe które mogą indeksowane przy użyciu przedmiotów jak klucze. Jeśli jesteś zainteresowany wiedząc więcej, dobry punkt wyjścia może być ten post:

Are HLists nothing more than a convoluted way of writing tuples?

Powiązane problemy