我正在尝试下面的Java代码在scala中将其转换,但是我无法从scala的子类中调用父类(super class)构造函数。我该如何解决这个问题。

//java code
class A
{
    public A(String a){}
    public A(String a, int b){}
}
class B extends A
{
    public B(String c){
        super(c);
    }
    public B(String c,int d){
        super(c,d);//how to do this in scala
    }
}

最佳答案

您会从一些有关构造函数的基本scala教程中受益,但让我尝试一下。
简而言之-您无法在scala中完全做到这一点。

  • 与Java中不同,Scala中只有一个“主要”构造函数,所有其他构造函数最终都必须链接到它。如果您的主要构造函数定义为class Foo(x: Int, y: Int, s: String) extends Bar(s),则可以在Foo中定义另一个构造函数,但是它必须像def this(x: Int) = this(x, 0, "")那样回链到其主要构造函数
  • 在Scala中,表达调用 super 构造函数的方式是在类定义的extends Bar(...)部分中(因此,在定义主构造函数和类的过程中,您只能执行一次此操作)。使用与上述相同的示例,如果您具有class Bar(s: String)并将其扩展为class Foo(val x: Int, val y: Int, s: String) extends Bar(s),则在声明要扩展它的同时调用 super 构造函数,即extends Bar(s)部分。

  • 因此,鉴于上述两点,由于所有构造函数都必须链接到类的单个主要构造函数,并且该单个主要构造函数仅调用单个 super 构造函数,因此您无法在Scala中准确地翻译Java示例。但是,您可以通过执行此操作获得等效的结果。
    final class Foo(x: Int, y: Int, s: String) extends Bar(s) {
         def this(x: Int) = this(x, 1/*Foo's default*/, ???/*you likely want the same default that super would use, in this case "bar", see below*/)
    }
    
    class Bar(s: String) {
         def this() = this("bar")
    }
    

    实际上,在scala中,您首先要进行链接,并在应用过程中应用所有默认值,然后再调用super而不是像在Java中那样从各种构造函数版本中调用super。

    您可以将 super 构造函数的默认值存储在伴随的object Bar { defaultString: String = "bar" }中,并在FooBar中使用该默认值,以确保impl保持同步。这似乎很笨拙,但实际上更安全,因为无论您调用哪个构造函数,它都能确保相同的行为,而在Java中,您可能具有不同的默认值(这将是有问题的)。这里是:
    final class Foo(x: Int, y: Int, s: String) extends Bar(s) {
         def this(x: Int) = this(x, 1/*Foo's default*/, Bar.defaultString)
    }
    
    class Bar(s: String) {
         def this() = this(Bar.defaultString)
    }
    object Bar {
        protected val defaultString: String = "bar"
    }
    

    但是请注意,在scala中-由于伴随对象和应用方法的建立良好的设施(您可以将它们视为Java中的静态工厂方法),因此我看不到人们使用很多非主要的构造函数。他们改用几种apply方法,因此您的主要构造函数通常采用所有可能的参数。另一种选择是让它们使用默认值。因此,您很少需要非主要构造函数和链接。

    与此相关的是,如果您具有直接传递给您的父类(super class)的构造函数参数,请注意不要包含val,因为这也会在子类中复制该变量,并增加您的内存占用量。这有点微妙。此外,如果您碰巧在主ctor中引用的变量前没有val,但在父类(super class)中不可见,则编译器将假定该变量具有private[this] val修饰符,并将其另存为字段在子类中。让我举例说明
    class A(val name: String, val counter: Int)
    
    class B(
       val b: Int /*this is B's field by design*/,
       str: String/*meant to be passed to super, so no val*/,
       i: Int /*meant to be passed to super, so no val*/) extends A(str, i) {
    
       def firstLetterOfName = str.head // Oops, I should have done name.head
    }
    

    由于我指的是出现在B的构造函数中的str而不是A字段的name,因此str将获得默认的private[this] val修饰符,并将其另存为B的字段,从而从父类(super class)name中复制了A字段。现在,我们有两个重复的字段。

    关于scala - 如何从Scala的子类中调用父类(super class)构造函数以及如何进行构造函数链接,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/38160948/

    10-11 20:41
    查看更多