【问题标题】:How to copy subset of fields in scala without boilerplate code如何在没有样板代码的情况下复制scala中的字段子集
【发布时间】:2014-08-26 02:33:13
【问题描述】:

我有以下课程:

class C {
   c1: Int,
   c2: Int,
   c3: String,
   ...
   c40: Long
}

class A extends C {
   a1: Int
}

class B extends C {
   b1: Int,
   b2: String
}

然后我需要在 A 的实例的基础上创建 B 的实例,并复制从 C 继承的字段的所有值,例如:

new B(50, "S", c1 = a.c1, c2 = a.c2, ..., c40 = a.c40)

但我想避免编写样板,例如:c1 = a.c1, c2 = a.c2, ..., c40 = a.c40

如何以 scala 方式有效地完成此操作,但仍保持静态定义的字段名称(不使用映射或其他无模式方法)?

一种解决方案是使用组合:

class A {
  a1: Int,
  base: C
}

class B {
  base: C,
  b1: Int,
  b2: String
}

然后写:new B(50, "S", base = a.base) 但在这种情况下,在所有其他代码中,如果我需要引用基础部分的值,我需要编写 b.base.c1 而不是 b.c1 - 我也想避免这种情况。

【问题讨论】:

  • 在所有其他代码中,如果我需要参考基础部分的值,我需要编写 b.base.c1 而不是 b.c1 你可以做 import b.base._ 并参考毫不费力地到达那个领域。
  • 这种方法只有在你只使用一个实体并在几个地方处理它时才有效。
  • 这两个答案对你有用吗?您是否需要任何人作出任何澄清?

标签: scala inheritance copy boilerplate


【解决方案1】:

您可以尝试使用一种技巧,即使用案例类的tupled 函数。我可以想象您可以使用unapply 提取源类的字段,附加新参数并使用tupled 函数从扩展元组创建新类。

其中一个示例展示了如何使用无形 shapeless tupples 很好地组合元组

这听起来像个黑客。

【讨论】:

    【解决方案2】:

    这可以通过稍微改变你的前置解决方案来完成。我将使用一个技巧,其中A 不是C 的子类,而是一个隐式子类型。

    class C(val c1: Int, val c2: Int)
    /*
     * A does not extend C in this example.
     * Using val to provide getters
     */
    class A(val a1: Int, val base: C)
    
    /*
     * B does not extends C or A
     */
    class B(val b1: Int, val base: C)
    
    /*
     * This is where the magic happens. This implicit will
     * convert our A to a C and provide the values for C
     */
    implicit def A2C(a: A): C = a.base
    
    val myC = new C(1, 2)
    // Wrap a C with our A
    val myA = new A(3, myC)
    
    // The implicit makes it seem that we are calling c1 from A
    println(myA.c1)
    
    // Now we can build a B from an A without any boilerplate
    val myB = new B(4, myA)
    
    // We need an implicit if we want to use a B as if it were a C
    implicit def B2C(b: B): C = b.base
    
    // And call C members without a messy b.base.c1 calls
    println(myB.c1)
    
    /*
     * Now assume A extended C because you need it to be a sub-type. This is
     * where a 'gotcha' raises its ugly head 
     */
    
    // A will be considered a C in this case
    def useSomeC(value: C) {
      println(value.c2)
    }
    
    // Use an A as if it were an actual sub-class of C
    useSomeC(myA)
    
    /*
     * But Generics mixes everything up for us
     */
    def useGeneric[Sub <: C](value: Sub) {
      println(value.c2)
    }
    
    // Does not compile. A is not an actual sub-type of C
    useGeneric(myA)
    
    /*
     * We can get around this a couple of ways. If we are using
     * someone else's methods and they expect Sub <: C then we
     * can force the implicit conversion before we make the call
     */
    val convertedToC: C = myA
    useGeneric(convertedToC)
    
    // Which can be cleaned up a bit like this
    useGeneric(myA: C)
    
    /*
     * But Scala does provide us with a tool to avoid this in our
     * own methods.
     * Note the <% instead of the <:
     */
    def useSpecial[Sub <% C](value: Sub) {
      println(value.c2)
    }
    
    // Compiles and works
    useSpecial(myA)
    

    我希望这个例子有所帮助。

    【讨论】:

      猜你喜欢
      • 2021-02-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-03
      • 2022-06-26
      • 2018-07-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多