如何在没有样板代码的情况下复制scala中的字段子集

时间:2014-07-04 18:53:34

标签: scala inheritance copy boilerplate

我有以下课程:

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 - 我也想避免这种情况。

2 个答案:

答案 0 :(得分:0)

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

其中一个示例展示了如何使用无形shapeless tupples

很好地组合元组

虽然听起来像是黑客。

答案 1 :(得分:0)

这可以通过稍微更改您的前置解决方案来完成。我将使用一个技巧,其中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)

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