首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >模块化Scala设计:如何避免构造函数“推出”样板?

模块化Scala设计:如何避免构造函数“推出”样板?
EN

Stack Overflow用户
提问于 2012-09-04 16:09:15
回答 3查看 272关注 0票数 12

也许一位具有良好风格和优雅意识的Scala专家可以帮助我找出一种更好的方法来构建以下代码,它有一个构造函数“推出”问题。

我们从一个简单的基类开始:

代码语言:javascript
复制
class Foo(val i: Int, val d: Double, val s: String) {

  def add(f: Foo) = new Foo(i + f.i, d + f.d, s + f.s)
  override def toString = "Foo(%d,%f,%s)".format(i,d,s)

}

为了在复杂的应用程序中进行类型检查,我需要一个不带任何附加状态的子类:

代码语言:javascript
复制
class Bar(i: Int, d: Double, s: String) extends Foo(i,d,s) {

  override def toString = "Bar(%d,%f,%s)".format(i,d,s)

}

现在,当我添加两个栏时,我只能得到一个Foo:

代码语言:javascript
复制
val x = new Bar(1,2.3,"x")
val y = new Bar(4,5.6,"y")
val xy = x.add(y)

REPL中的响应如下:

代码语言:javascript
复制
x  : Bar = Bar(1,2.300000,x)
y  : Bar = Bar(4,5.600000,y)
xy : Foo = Foo(5,7.900000,xy)

如何以一种优雅的方式将两个栏添加到一起以形成另一个Bar (而不是Foo),而不必复制和粘贴Foo的add方法,如下所示?

代码语言:javascript
复制
class Bar(i: Int, d: Double, s: String) extends Foo(i,d,s) {

  // ugly copy-and-paste from Foo:
  def add(b: Bar) = new Bar(i + b.i, d + b.d, s + b.s)
  override def toString = "Bar(%d,%f,%s)".format(i,d,s)

}

我有很多这样的栏(基本上都是Foo的副本,但对于类型检查非常重要),一个无需剪切和粘贴的解决方案将会带来好处。

谢谢!

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-09-04 17:00:21

我尽量避免继承。因此,这里有一种替代方法。

Bar具有与Foo完全相同的构造函数,并且两者都是无状态的。如果你想有几个子类型,只是为了传达任何额外的信息,你可以使用一个泛型参数作为“标签”。例如:

代码语言:javascript
复制
trait Kind
trait Bar extends Kind

class Foo[T<:Kind](val i: Int, val d: Double, val s: String) {
   def add(f: Foo[T]) = new Foo[T](i + f.i, d + f.d, s + f.s)
   override def toString = "Foo(%d,%f,%s)".format(i,d,s)
}


scala> val b1 = new Foo[Bar](2,3.0,"hello")
b1: Foo[Bar] = Foo(2,3.000000,hello)

scala> val b2 = new Foo[Bar](3,1.0," world")
b2: Foo[Bar] = Foo(3,1.000000, world)

scala> b1 add b2
res1: Foo[Bar] = Foo(5,4.000000,hello world)

现在add是类型安全的。然后,您可以使用一个类型类来获取显示KindtoString

票数 12
EN

Stack Overflow用户

发布于 2012-09-05 03:44:08

扩展@ Expanding的答案,如果你想支持特定于每个Bar的操作(例如,不同的toString),你可以更进一步,使Kind成为一个类型类。

代码语言:javascript
复制
trait Kind[T] { def name : String }
trait Bar
implicit object BarHasKind extends Kind[Bar] { val name = "Bar" }

class Foo[T : Kind](val i : Int, val d : Double, val s : String) {
  def add(f : Foo[T]) = new Foo[T](i + f.i, d + f.d, s + f.s)
  override def toString = implicitly[Kind[T]].name + "(%d,%f,%s)".format(i,d,s)
}

scala> val b1 = new Foo[Bar](2, 3.0, "hello")
b1: Foo[Bar] = Bar(2,3.000000,hello)

trait Biz
implicit object BizHasKind extends Kind[Biz] { val name = "Biz" }

scala> val b2 = new Foo[Biz](1, 1.0, "One")

它和以前一样是类型安全的:

代码语言:javascript
复制
scala> b1 add b2
<console>:16: error: type mismatch;
  found   : Foo[Biz]
  required: Foo[Bar]

scala> b2 add b2
resN: Foo[Biz] = Biz(2,2.000000,OneOne)

对于您希望依赖于标记的任何属性,请在Kind中抽象地声明它们,并在隐式对象中提供实现。

票数 5
EN

Stack Overflow用户

发布于 2012-09-07 21:20:20

使用类型参数化的方法有一个限制,因为它不允许您以自然的方式扩展功能,这就是继承。因此,另一种方法可以是可重写的工厂方法(这只是构造函数的委托):

代码语言:javascript
复制
class Foo(val i: Int, val d: Double, val s: String) {

  protected def create(i: Int, d: Double, s: String) = new Foo(i, d, s)

  def add[A <: Foo](f: A) = create(i + f.i, d + f.d, s + f.s)

  override def toString = "Foo(%d,%f,%s)".format(i,d,s)
}

class Bar(i: Int, d: Double, s: String) extends Foo(i,d,s) {

  protected override def create(i: Int, d: Double, s: String) = new Bar(i, d, s)

  override def toString = "Bar(%d,%f,%s)".format(i,d,s)

  // additional methods...

}

println( new Foo(10, 10.0, "10") add new Bar(10, 10.0, "10") )
println( new Bar(10, 10.0, "10") add new Foo(10, 10.0, "10") )
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12259254

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档