在Scala中,我如何继承一个具有多个构造函数的Java类?

60

假设我有一个Java类,其中包含多个构造函数:

class Base {
    Base(int arg1) {...};
    Base(String arg2) {...};
    Base(double arg3) {...};
}

我该如何在Scala中扩展它并仍然提供对Base的所有三个构造函数的访问?在Scala中,子类只能调用其超类的一个构造函数。有什么方法可以规避这个限制吗?

假设Java类是无法更改的遗留代码。

4个回答

95

很容易忘记一个特质可以扩展一个类。如果你使用一个特质,你可以推迟决定调用哪个构造函数,像这样:

trait Extended extends Base {
  ...
}

object Extended {
  def apply(arg1: Int) = new Base(arg1) with Extended
  def apply(arg2: String) = new Base(arg2) with Extended
  def apply(arg3: Double) = new Base(arg3) with Extended
}

Scala 2特质本身可能没有构造函数参数,但是您可以通过使用抽象成员来解决这个问题。

(Scala 3允许特质有构造函数参数.)


1
如果Extended有额外的参数,或者我想用Grandchild扩展Extended呢? - tuxSlayer
很想看一下如何“通过使用抽象成员来解决这个问题”的示例。 - Adam Mackler
@AdamMackler:将class A(val b:B)替换为trait A { def b:B }。在这里使用def通常比使用val更安全,因为存在初始化顺序陷阱。要实例化,而不是new A(b),您可以执行new A { val b = ... }。(val可能会覆盖def。) - Seth Tisue
1
请注意,这并不等同于您无法使用新键盘实例化Extended。根据此链接,似乎没有办法以Java的方式进行等效操作。 - OlivierBlanvillain
如果扩展的整个目的是访问受保护的值,那么这并不起作用。 - kap

5

编辑 - 这是从Scala邮件列表中的一个问题,我认为在这里重复了。我的答案涉及提供三个不同的构造函数(即复制Java设计),而不是扩展类

假设您的每个构造函数最终都会创建对象的状态S,请创建一个伴随对象,并使用“静态”方法创建此状态。

object Base {
  private def stateFrom(d : Double) : S = error("TODO")
  private def stateFrom(s : Str) : S = error("TODO")
  private def stateFrom(i : Int) : S = error("TODO")
} 

然后创建一个私有构造函数,接受状态和(公共的)重载构造函数,这些构造函数都将推迟到主构造函数。
import Base._
class Base private(s : S) { //private constructor takes the state
  def this(d : Double) = this(stateFrom(d)) 
  def this(str : String) = this(stateFrom(str))
  def this(i : Int) = this(stateFrom(i))
  //etc
}

你正在更改基础,因此这回答了一个完全不同的问题。尽管如此,它是那个其他问题的好答案 :-) - Seth Tisue
是的 - 邮件列表上的问题没有这个问题写得清楚! - oxbow_lakes
那不是问题。 - Arun George

2
我会选择最通用的类型(在这种情况下是String),如果它符合其他条件,就自己进行内部转换。虽然我承认这不是最好的解决方案,但我感觉有些不对劲。 :-(

错误的地方在于它假设“最通用的”是一个有效的概念,但实际上并不是。不同的构造函数可以完成完全不同的任务。 - James Moore
@JamesMoore 点非常好。显然,人们需要了解各种构造函数的行为以影响您的决策。但是,如果给定 double d,那么 new Base(d);new Base((int)d);new Base(String.valueOf(d)); 都不做同样的事情,这将是最少惊讶原则的严重违反。当然这并不是保证,但如果它们在做不同的事情,那么应该有不同的类。现在,如果只有所有代码都遵循可预测的设计原则... :) - corsiKa

2

这是一个有些愚蠢的答案,可能能够起到一定作用,但如果Java类有太多构造函数,则可能需要付出过多的努力:

在Java中编写一个子类,实现一个构造函数,该构造函数获取各种其他构造函数的所有输入,并根据输入的存在或不存在(通过使用“null”或某种标记值)调用其超类的适当构造函数,然后在Scala中对该Java类进行子类化,并将标记值分配为默认参数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接