Scala: Получить аргументы, используемые для построения экземпляра класса наследуемым способом?

в scala, у меня есть ситуация, когда базовый класс/признак расширяется очень многими дочерними классами. Я собираюсь сериализовать экземпляр дочернего класса (с целью его восстановления позже), получив имя класса и аргументы, используемые для создания экземпляра класса. легко получить имя экземпляра класса с помощью отражения, но я не уверен, как получить аргументы экземпляра таким образом, чтобы не требовать, чтобы я писал код в каждом дочернем классе. это необходимо также для аргументов, называемых по имени. это кажется сложным, потому что каждый дочерний класс может иметь совершенно разные числа и типы аргументов конструктора:

abstract class Base {
  // ideally implements some way of finding out the arguments used to construct this object
  def getArgs = {...}
}

class ChildA() // args would be a 0-tuple

class ChildB(arg1: Int) // getArgs would be a 1-tuple of arg1

class ChildC(arg1: Double, arg2: Boolean) // args would be a 2-tuple of (arg1, arg2)

// etc... 

что я хочу избежать, каждый класс реализует метод типа getArgs и просто возвращает кортеж, дублирующий аргументы от конструктора. любой способ сделать это?

Я понимаю, что альтернативный подход состоит в том, чтобы иметь какой-то перехватчик для создания объекта, но я не мог понять, как это сделать без необходимости писать шаблонный код для каждого дочернего класса.

edit: подход класса case элегантен, но в моей ситуации не подходит, потому что мы должны уметь разрешать конкретным дочерним классам распространять другие конкретные дочерние классы.

Ответ 1

Если ваша цель - не писать код в каждом дочернем классе для захвата аргументов, а все дочерние классы находятся в плоской иерархии, непосредственно наследуемой от Base, а не друг от друга, вы должны рассмотреть возможность объявления их как case class вместо этого. Затем они автоматически расширят Product, и вы можете использовать и productIterator, например. сохраните все args в Array[Any] (я использую Array, потому что это необходимо, когда вы рефлексивно вызываете конструктор этих классов позже):

scala> def toArray(p: Product) = p.productIterator.toArray
toArray: (p: Product)Array[Any]

scala> case class ChildA()
defined class ChildA

scala> toArray(ChildA())
res0: Array[Any] = Array()

scala> case class ChildB(arg1: Int)
defined class ChildB

scala> toArray(ChildB(42))
res1: Array[Any] = Array(42)

Если вы хотите, чтобы toArray принимал только подклассы Base, тогда объявите его как Product with Base.