如何从scala中的子类调用超类构造函数以及如何进行构造函数链接 [英] How to call superclass constructor from child class in scala and how to do constructor chaining

查看:31
本文介绍了如何从scala中的子类调用超类构造函数以及如何进行构造函数链接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在下面的java代码中在scala中转换它,但我无法从scala中的子类调用超类构造函数.我该如何解决这个问题.

I was trying below java code to convert it in scala but i was not able to call superclass constructor from child class in scala. How do i solve this problem.

//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 中完全做到这一点.

You would benefit from some basic scala tutorial on constructors but let me try. In short -- you cannot do this in scala exactly.

  1. 与 Java 不同,Scala 中只有一个主"构造函数,所有其他构造函数最终都必须链接到它.如果您的主构造函数定义为 class Foo(x: Int, y: Int, s: String) extends Bar(s) 您可以在 Foo 中定义另一个构造函数,但它必须链接回其主构造函数因此 def this(x: Int) = this(x, 0, "")
  2. 在 Scala 中,您表示调用超级构造函数的方式是在类定义的 extends Bar(...) 部分中(因此您只能在定义的过程中这样做一次您的主要构造函数和类).使用与上面相同的例子,如果你有 class Bar(s: String) 并且你扩展它 class Foo(val x: Int, val y: Int, s: String) extends Bar(s) 您在声明要扩展它的同时调用超级构造函数,即 extends Bar(s) 部分.
  1. Unlike in Java, in Scala there is a single 'primary' constructor and all other constructors must eventually chain to it. If your primary constructor is defined as class Foo(x: Int, y: Int, s: String) extends Bar(s) you could define another constructor in Foo but it must chain back to its main constructor as such def this(x: Int) = this(x, 0, "")
  2. In Scala, the way you express calling the super constructor is in the extends Bar(...) portion of your class definition (and so you can only do that that once as part of defining your primary constructor and class). Using the same example as above, if you have class Bar(s: String) and you extend it class Foo(val x: Int, val y: Int, s: String) extends Bar(s) you are invoking the super constructor at the same time as you are declaring that you are extending it, ie the extends Bar(s) part.

因此,鉴于以上两点,由于所有构造函数都必须链接到类的单个主构造函数,并且该单个主构造函数仅调用单个超级构造函数,因此您无法在 Scala 中准确地翻译 Java 示例.但是,您可以通过这样做获得等效的东西.

So given those two points above, since all constructors must chain to the single primary constructor of a class AND that single primary constructor only invokes a single super constructor, you cannot exactly translate your Java example in Scala. However, you can get something equivalent by doing this.

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.

In effect, in scala you chain first, applying all defaults as you go, and then you call the super rather than calling super from your various constructor versions as you can in Java.

您可以将超级构造函数的默认值存储在伴随的 object Bar { defaultString: String = "bar" } 中,并在 Foo 中使用它Bar 确保实现保持同步.这看起来很笨重,但实际上更安全,因为无论您调用哪个构造函数,它都能确保相同的行为,而在 Java 中,您可能有不同的默认值(这可能是有问题的).这是:

You COULD store the super constructor default value in a companion object Bar { defaultString: String = "bar" } and use that both in Foo and in Bar to ensure that the impl remain in sync. This seems clunky but is actually safer because it ensures identical behavior regardless of which constructor you invoke, while in java you might have different defaults (which would be questionable). Here it is:

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 方法代替,因此您的主构造函数通常采用所有可能的参数.另一种选择是让它们使用默认值.所以你很少需要非主构造函数和链接.

However note that in scala -- because of the well established facility of companion objects and apply methods (you can think of them as static factory methods in Java), I do not see people using a lot of non-primary constructors. They use several apply methods instead so your primary constructor generally takes all possible parameters. Another option is to have them use default values. So you very rarely need non-primary constructors and chaining.

在相关说明中,如果您有直接传递给超类的构造函数参数,请注意不要包含 val,因为这也会复制子类中的变量,并增加你的内存占用.这有点微妙.此外,如果您碰巧在主 ctor 中引用了一个在其前面没有 val 但在超类中不可见的变量,编译器将假定它有一个 private[this] val 修饰符,并将其保存为子类中的字段.我来举例说明

On a related note, if you have constructor parameters that you are passing directly to your super class, be careful not to include val as that would duplicate the variable in the subclass as well, and increase your memory footprint. It is kind of subtle. Additionally, if you happen to refer to a variable that does NOT have val in front of it in your primary ctor, but is not visible from the super class, the compiler will assume it has a private[this] val modifier and will also save it as a field in the sub class. Let me illustrate

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 的字段,从而复制超类 A<中的 name 字段/代码>.现在我们有两个重复的字段.

Since I referred to str which appears in B's constructor rather than name which is A's field, str will get a default private[this] val modifier and will be saved as B's field as well, thus duplicating the name field from the super class A. Now we have two fields that are duplicative.

这篇关于如何从scala中的子类调用超类构造函数以及如何进行构造函数链接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆