class C {
private[this] var x: Int = _
}
object Test {
def main(args: Array[String]): Unit = {
val field = classOf[C].getDeclaredField("x")
println(field)
}
}
[error] java.lang.NoSuchFieldException: x
[error] at java.base/java.lang.Class.getDeclaredField(Class.java:2411)
[error] at Test$.main(Main.scala:7)
[error] at Test.main(Main.scala)
[error] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[error] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[error] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[error] at java.base/java.lang.reflect.Method.invoke(Method.java:566)
It print:
private int C.x
I don't know if this is an expected behaviour or a bug?
@ohze did you encounter an issue because of this behaviour? In the case you show, we can tell that the field is never accessed by "normal" code, so removing it seems reasonable. OTOH, I understand that this can be problematic if the field is only ever accessed with reflection, and we could consider adding an annotation which prevents the field from being removed.
Related issue in Scala 2 https://github.com/scala/bug/issues/10400
The behavior is intentional. The use case is where you define a private val that is only used in the primary constructor. E.g.
class C(x: Int, y: Int):
private val d = gcd(x, y)
val normX = x/d
val normY = y/d
In this case, you'd like the field not to be retained.
If the field must be retained at runtime, make it private[C].
Inferred private[this] suggests a different idiom is needed for explicit field. Private reference by companion suffices but is verbose. Maybe presence of explicit type to signal retention? (by loose analogy to constant value.) The other analogy is explicit type for API, where the client in this case is by reflection.
There's already an @transient annotation so having a dual @retained could make sense. This annotation could also be used in objects to mark fields that should actually be serialized as I proposed in https://github.com/lampepfl/dotty/issues/5135
Changed scala 2 behavior at https://github.com/scala/scala/pull/9226