英文:
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'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 def
s, parent val
s 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"${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 def
s, parent val
s 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
, thenBar
)
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 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
, thenBar
)
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 = "something"
val value2 = "else"
def concatValues = s"$value1 $value2"
}
case class Bar(_barValue: String = "") extends Foo {
val barValue = if (_barValue.isEmpty) concatValues else _barValue
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论