其元素可以使用命名参数和默认值构造兄弟实例的 Scala 集合?

Scala collection whose elements can construct sibling instances using named parameters and default values?

我想要一个对象集合,每个对象都是不同 class 的同伴,其中 class 都共享一个在 superclass 中定义的通用方法,可以在使用 foreach() 遍历集合时调用。我希望这些同级 class 的构造函数彼此具有相同的命名参数 和默认参数值 。最后,我想尽量减少重复代码。

到目前为止,我正在尝试使用案例 classes 执行此操作,因为——如果它有效——它将消除每种类型的伴随对象的所有重复代码。问题是,如果我将所有这些伴随对象放入 Set,当我再次将它们取出时,我会丢失默认参数和参数名称。

这是我所描述的一些示例代码:

trait MyType {
  val param: String
  def label = param   // all instances of all subclasses have this method
}

case class caseOne(override val param: String = "default") extends MyType
case class caseTwo(override val param: String = "default") extends MyType

object Main extends App {
  // I can construct instances using the companion objects' `apply()` method:
  val works1 = caseOne(param = "I have been explicitly set").label
  // I can construct instances that have the default parameter value
  val works2 = caseOne().label

  // But what I want to do is something like this:
  val set = Set(caseOne, caseTwo)
  for {
    companion <- set
  } {
    val fail1 = companion()                      // Fails to compile--not enough arguments
    val fail2 = companion(param = "not default") // Fails also as param has lost its name
    val succeeds = companion("nameless param")   // this works but not what I want
    println(fail1.label + fail2.label)           // this line is my goal
  }
}

值得注意的是,如果 Set 只有一个元素,那么它会编译,提示多元素的推断类型 Set 缺少参数名称——即使它们是相同的——和默认值。还建议如果我给 Set 正确的类型参数,这可能会起作用。但是那种类型是什么?不是 MyType,因为那是伴随 class 的类型,而不是 Set.

中的对象

我可以显式定义伴随对象,但这是我想避免的重复代码。

我如何循环遍历我的集合,在每次迭代中构造 MyType subclasses 的实例,并使用具有我想要的参数名称和默认值的构造函数?同时尽量减少重复代码?

更新: 最初示例代码显示 caseOnecaseTwo 具有不同的 param 默认值。那是不正确的;他们现在是一样的。

您将无法准确获得所需内容,因为您对自动生成的伴生对象没有太多控制权。特别是要使其发挥作用,他们都需要扩展一个共同特征。这就是为什么当集合有多个伴生对象时编译失败的原因;即使它们都有一个具有相同签名的方法,它们也没有扩展编译器可以利用的共同特征。

您可以使用嵌套案例 class 并得到非常相似的东西:

trait MyType {
  val param: String
  def label = param   // all instances of all subclasses have this method
}

abstract class MyTypeHelper(default: String) {

  case class Case(param: String) extends MyType

  def apply(param: String) : Case = Case(param)
  def apply(): Case = apply(default)
}

object One extends MyTypeHelper("default one")
object Two extends MyTypeHelper("default two")

object Example {

  val works1 = One(param = "I have been explicitly set").label
  val works2 = One().label

  val set = Set(One, Two)
  for {
    companion <- set
  } {
    val a = companion()                      
    val b = companion(param = "not default") 
    val c = companion("nameless param")   
    println(a.label + b.label)           
  }
}

您没有 caseOne 类型,而是 One.Case,但它仍然实现 MyType,因此您在使用该特征的代码中的其他任何地方都不应该有任何问题.