在子类的构造函数中调用父类的字段是否可能?

huangapple go评论64阅读模式
英文:

Is it possible to invoke parent fields in constructor of child class?

问题

例如如果我有

```scala
sealed trait Foo {
   val value1 = "something"
   val value2 = "else"
}

我可以得到类似这样的东西吗?

case class Bar(barValue = s"${super.value1} ${super.value2}") extends Foo

目前我遇到了编译错误:value barValue is not a member of AnyRef

PS 似乎这很容易做到,但我不知道为什么会出现这个编译错误
这是一个有趣的案例,因为这两个 val 在构造函数中都是可见的,但编译器失败了


<details>
<summary>英文:</summary>

For example if I have

sealed trait Foo {
val value1 = "something"
val value2 = "else"
}


Can I get something like this?


case class Bar(barValue = s"${super.value1} ${super.value2}") extends Foo


For now I have a compiler error: *value barValue is not a member of AnyRef*

PS Seems it like be easy to do, but I didn&#39;t catch why I got this compiler error
It is interesting case cause both vals are visible in constructor, but compiler fails

</details>


# 答案1
**得分**: 4

I can't reproduce `value barValue is not a member of AnyRef`. I can reproduce `value value1 is not a member of AnyRef`, `value value2 is not a member of AnyRef`.

https://scastie.scala-lang.org/DmytroMitin/BM6uIxGhTtmIUZoex820aA/1

A workaround is to make the case class an ordinary class and define factory methods

```scala
class Bar private extends Foo {
  val barValue: String = s"${value1} ${value2}"
}
object Bar {
  def apply(): Bar = new Bar
  def apply(_barValue: String): Bar = new Bar {
    override val barValue: String = _barValue
  }
}

Bar().barValue      // something else
Bar("abc").barValue // abc

Or it can be a case class

case class Bar() extends Foo {
  val barValue: String = s"${value1} ${value2}"
}
object Bar {
  def apply(_barValue: String): Bar = new Bar {
    override val barValue: String = _barValue
  }
}

Although there is not much sense in keeping the class a case class. equals, hashcode, toString etc. will be generated without respect to barValue.

On contrary to parent defs, parent vals should be accessed without super.

The error value1 is not a member of AnyRef is understandable. Default values of constructor parameters are typechecked in outer scope, not in parent scope. And probably you put everything into some object. super refers to the super of this outer object, i.e. AnyRef. And AnyRef doesn't have member value1. If you make everything top-level then the error changes to this can be used only in a class, object, or template.

英文:

I can't reproduce value barValue is not a member of AnyRef. I can reproduce value value1 is not a member of AnyRef, value value2 is not a member of AnyRef

https://scastie.scala-lang.org/DmytroMitin/BM6uIxGhTtmIUZoex820aA/1

A workaround is to make the case class an ordinary class and define factory methods

class Bar private extends Foo {
  val barValue: String = s&quot;${value1} ${value2}&quot;
}
object Bar {
  def apply(): Bar = new Bar
  def apply(_barValue: String): Bar = new Bar {
    override val barValue: String = _barValue
  }
}

Bar().barValue      // something else
Bar(&quot;abc&quot;).barValue // abc

Or it can be a case class

case class Bar() extends Foo {
  val barValue: String = s&quot;${value1} ${value2}&quot;
}
object Bar {
  def apply(_barValue: String): Bar = new Bar {
    override val barValue: String = _barValue
  }
}

Although there is not much sense in keeping the class a case class. equals, hashcode, toString etc. will be generated without respect to barValue.

On contrary to parent defs, parent vals should be accessed without super.

The error value1 is not a member of AnyRef is understandable. Default values of constructor parameters are typechecked in outer scope, not in parent scope. And probably you put everything into some object. super refers to the super of this outer object, i.e. AnyRef. And AnyRef doesn't have member value1. If you make everything top-level then the error changes to this can be used only in a class, object, or template.

答案2

得分: 2

I'd say no, because case class Bar creates not only Bar constructor (new Bar) but also factory method in companion object Bar.apply) and the latter cannot access Foo's constructor, even if Bar could.

And Bar can access super in the body, but not necessarily in the default value, since the default value... underneath is also a method in a companion.

Bar() // is seen by JVM as more or less as
Bar.apply(Bar.$init$1) // creates the default value and pass it to apply

And even if this method can use something passed from the outside (e.g. copy uses existing values from copied object) here you:

  • fetch the default values
  • call the constructor using these values
  • run the body of the constructor (first Foo, then Bar)

so the default values are created long before val value1 and val value2 are initialized. You'd have to store it somewhere else to make them accessible.

英文:

I'd say no, because case class Bar creates not only Bar constructor (new Bar) but also factory method in companion object Bar.apply) and the later cannot access Foo's constructor, even if Barcould.

And Bar can access super in the body, but not necessarily in the default value, since the default value... underneath is also a method in a companion.

Bar() // is seen by JVM as more or less as
Bar.apply(Bar.$init$1) // creates the default value and pass it to apply 

And even if this method can use something passed from the outside (e.g. copy uses existing values from copied object) here you:

  • fetch the default values
  • call the constructor using these values
  • run the body of the constructor (first Foo, then Bar)

so the default values are created long before val value1 and val value2 are initialized. You'd have to store is somewhere else to make them accessible.

答案3

得分: 1

不可以在案例类字段的默认值中使用 super。但是,您可以使用以下方法来实现所需的效果:

sealed trait Foo {
  val value1 = "something"
  val value2 = "else"

  def concatValues = s"$value1 $value2"
}

case class Bar(_barValue: String = "") extends Foo {
  val barValue = if (_barValue.isEmpty) concatValues else _barValue
}
英文:

No, you can't use super in default values for case class fields. However, you can achieve the desired effect using the following approach:

sealed trait Foo {
  val value1 = &quot;something&quot;
  val value2 = &quot;else&quot;

  def concatValues = s&quot;$value1 $value2&quot;
}

case class Bar(_barValue: String = &quot;&quot;) extends Foo {
  val barValue = if (_barValue.isEmpty) concatValues else _barValue
}

huangapple
  • 本文由 发表于 2023年3月31日 17:06:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/75896686.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定