Получить значения параметров конструктора, используя отражение scala

Работа в кодовой базе со scala, которая хочет, чтобы вы для определенных классов определили своего рода «создать новую версию» - например, если у вас есть класс x (a: int, b: String, c: double).. , у него будет такая функция:

class x( a: Integer, b : String, c : Double) extends CanMakeNew
{
    def newValue() = x( a, b, c)
}

Я не контролирую это, но предпочел бы не реализовывать это каждый раз. Или... когда-либо. Есть ли способ в scala с отражением - перебирать значения параметров конструктора? Я могу использовать отражение для просмотра типов параметров, но, поскольку имена параметров не были включены для этого модуля, и я не могу его включить, я не могу сопоставить их с сохраненными значениями в класс. По сути, я ищу в любом случае реализацию такой черты, как:

trait CanMakeNewDoneForMe extends CanMakeNew {
    def newValue()  {I need the code that goes here}

Итак, есть ли у отражения scala какой-либо способ либо проверить конструктор, либо проверить объект и увидеть «ааа, это был третий параметр в конструкторе»?


person Darren Oakey    schedule 15.07.2019    source источник
comment
По какой причине newValue() не может просто вернуть this? Содержит ли x какое-либо изменяемое состояние, из-за которого возврат того же экземпляра может стать проблемой?   -  person Astrid    schedule 15.07.2019
comment
да - x содержит кучу изменяемого состояния.   -  person Darren Oakey    schedule 15.07.2019


Ответы (2)


Если вы сделаете X классом case, он будет иметь apply, copy... сгенерированный компилятором автоматически.

И, по сути, это не моя кодовая база, поэтому я не могу ничего изменить...

Когда вы делаете класс классом case, вы на самом деле не «меняете форму вещей», вы просто добавляете автоматически сгенерированные методы.

В любом случае, вы можете создать аннотацию макроса, которая генерирует метод newValue.

  import scala.annotation.StaticAnnotation
  import scala.language.experimental.macros
  import scala.reflect.macros.blackbox

  class newValue extends StaticAnnotation {
    def macroTransform(annottees: Any*): Any = macro newValueMacro.impl
  }

  object newValueMacro {
    def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
      import c.universe._
      annottees match {
        case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: tail =>
          val tparams1 = tparams.map {
            case q"$_ type $name[..$_] >: $_ <: $_" => tq"$name"
          }
          val paramss1 = paramss.map(_.map {
            case q"$_ val $pat: $_ = $_" => pat
          })
          q"""
              $mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self =>
                def newValue() = new $tpname[..$tparams1](...$paramss1)
                ..$stats
              }

              ..$tail
            """

        case _ => c.abort(c.enclosingPosition, "not a class")
      }

    }
  }

  @newValue
  /*case*/ class X(a: Int, b : String, c : Double) {
    override def toString: String = s"X($a, $b, $c)"
  }

  val x = new X(1, "a", 2.0) //X(1, a, 2.0)
  //  val x1 = x.copy()
  val x1 = x.newValue() //X(1, a, 2.0)
person Dmytro Mitin    schedule 15.07.2019
comment
хорошо - это очень круто - мне нужно время, чтобы понять это :) Спасибо - person Darren Oakey; 15.07.2019

Я могу ошибаться, но обычно это достигается с помощью сопоставления с образцом и методов apply() и unapply(), определенных в вашем объекте-компаньоне.

Я сделал небольшой тест вашего кода выше в сеансе REPL. Я не понимал назначение функции newvalue(), поэтому пропустил ее.

class x(val a: Integer, val b : String, val c : Double)
{
  //def newValue() = x( a, b, c)
}

object x {
  def apply(a: Integer, b: String, c: Double): x = new x(a,b,c)
  def unapply(m: x): Option[(Integer, String, Double)] = Some((m.a, m.b, m.c))
}

x(1, "hello", 99.0d) match {
  case l: x => println(s"this the the 3rd arg in the constructor: ${l.c}")
}

Определенная выше функция unapply() позволяет деконструировать совпадение с шаблоном на объекте. Альтернативой является использование case class для определения класса x (это определит для вас функции apply() и unapply()).

person Kevin Lawrence    schedule 15.07.2019
comment
Извините, я не был ясен. Вопрос в том, как мне реализовать функцию newvalue общим способом — это единственное, что имеет значение. - person Darren Oakey; 15.07.2019
comment
@DarrenOakey Почему вы не можете сделать X классом case? Он имеет apply, copy..., сгенерированные компилятором автоматически. - person Dmytro Mitin; 15.07.2019
comment
Потому что я не показал всей картины — это все сервисные классы, которые инициализируются в среде, а функция существует, чтобы их можно было инициализировать в другой среде. И в основном это не моя кодовая база, поэтому я не могу изменить форму вещей - я просто хочу найти умный способ не кодировать эту функцию каждый раз вручную - и я не могу представить, что существует решение это не связано с отражением :( - person Darren Oakey; 15.07.2019