Получение открытых полей (и их соответствующих значений) экземпляра в Scala / Java - PullRequest
10 голосов
/ 18 сентября 2011

PHP вводит метод, который позволяет вам выбирать все открытые значения экземпляра. Есть ли способ сделать это в Scala? То есть извлечь все значения всех открытых полей экземпляра класса (не объекта).

Давайте предположим, что у меня есть этот класс

class TestElement( datatype: Datatype, var subject: String, var day: Int, var time: Int )
  extends DataElement( datatype: Datatype ) {    
   def to( group: Group ) = group.add( this );
}

var element = new TestElement( datatype, "subject", 1, 1 );

Что мне нужно от рассматриваемого метода, так это получить карту или две коллекции значений.

var element.method                                       // the function I need
ret: ( ("subject", "subject"), ("day", 1), ("time", 1) ) // its output

Ответы [ 5 ]

21 голосов
/ 18 сентября 2011

Время спать, поэтому у меня нет времени на полный ответ, но посмотрите на результаты element.getClass.getFields (или getDeclaredFields для частных полей) - вы можете позвонить getValue(element) на Fieldобъекты для извлечения их значений.


Проснись сейчас, и все еще не лучший ответ, поэтому:

Во-первых, обратите внимание, что в терминах Java ваш класс не имеет субъекта открытого поля, то, что у него есть, это субъект частного поля и методы аксессора subject () и subject_ $ eq (String).

Вы можете перебирать объекты частного поля, как описано выше, заполняя Map из пар:

def getFields(o: Any): Map[String, Any] = {
  val fieldsAsPairs = for (field <- o.getClass.getDeclaredFields) yield {
    field.setAccessible(true)
    (field.getName, field.get(o)) 
  }
  Map(fieldsAsPairs :_*)
}

Теперь вы можете либо определить этот метод в TestElement (заменив o на this), либо в более общем смысле определить преобразование, чтобы вы могли вызывать getFields для любой ссылки

implicit def any2FieldValues[A](o: A) = new AnyRef {
  def fieldValues = getFields(o)
}

Так что

element.fieldValues 

даст желаемый результат.

3 голосов
/ 18 сентября 2011

A за ответ Филиппа, вы можете сделать это для кейсов.

В более широком смысле, та же самая техника работает для любого подкласса Product. Как и классы case, Tuples являются еще одним очевидным примером, но список гораздо более обширный, чем этот.

Взгляните на «известные подклассы», здесь: http://www.scala -lang.org / api / current / scala / Product.html

2 голосов
/ 18 сентября 2011

Вы можете сделать что-то относительно близкое к этому для классов дел:

case class SomeEntity(name : String, value : Int, misc : Boolean)
val s = SomeEntity("Tom", 42, false)
println(s.productIterator.map(_.toString).mkString(", ")) // "Tom, 42, false"

... как и следовало ожидать, productIterator выполняет итерации по элементам типа Any.Этот метод генерируется автоматически только для классов дел, и вы не получите имя поля.Для чего-то большего вам нужно будет использовать рефлексию, и для этого вам может потребоваться дождаться выхода 2.10.

1 голос
/ 19 сентября 2011

Просто примечание для тех, кто пытается улучшить это, сделав подход @duncan более сильным:

Вместо возврата Map[String, Any], где значение набрано как Любое, вы можете сделать следующее:

def propertiesAsPairs() = {
    val fields = (this.getClass.getDeclaredFields())
    for ( field <- fields ) yield {
        field.setAccessible( true );
        ( field.getName, field.get( this ) );
    }
}
0 голосов
/ 19 сентября 2011

Scala сознательно заставляет val, var и def использовать общий интерфейс, поэтому вы можете заменить первые два на последний, не нарушая никакого кода - даже не требуя перекомпиляции.

Так что, хотя можно делать то, что вы хотите, это приведет к хрупкому коду, что делает то, чего он не должен делать.

...