Consider the following base and derived classes in Scala:
abstract class Base( val x : String )
final class Derived( x : String ) extends Base( "Base's " + x )
{
override def toString = x
}
Here, the identifier 'x' of the Derived class parameter overrides the field of the Base class, so invoking toString like this:
println( new Derived( "string" ).toString )
returns the Derived value and gives the result "string".
So a reference to the 'x' parameter prompts the compiler to automatically generate a field on Derived, which is served up in the call to toString. This is very convenient usually, but leads to a replication of the field (I'm now storing the field on both Base and Derived), which may be undesirable. To avoid this replication, I can rename the Derived class parameter from 'x' to something else, like '_x':
abstract class Base( val x : String )
final class Derived( _x : String ) extends Base( "Base's " + _x )
{
override def toString = x
}
Now a call to toString returns "Base's string", which is what I want. Unfortunately, the code now looks somewhat ugly, and using named parameters to initialize the class also becomes less elegant:
new Derived( _x = "string" )
There is also a risk of forgetting to give the derived classes' initialization parameters different names and inadvertently referring to the wrong field (undesirable since the Base class might actually hold a different value).
Is there a better way?
Edit 1: To clarify, I really only want the Base values; the Derived ones just appear to be necessary for initializing the fields of the base class. The example only references them to illustrate the ensuing issues.
Edit 2: Actually, the example would have been clearer if I had used vars instead of vals, since that highlights the problem with values getting changed later on in the base class:
class Base( var x : Int ) { def increment() { x = x + 1 } }
class Derived( x : Int ) extends Base( x ) { override def toString = x.toString }
val derived = new Derived( 1 )
println( derived.toString ) // yields '1', as expected
derived.increment()
println( derived.toString ) // still '1', probably unexpected
Edit 3: It might be nice to have a way to suppress automatic field generation if the derived class would otherwise end up hiding a base class field. It would appear that the Scala compiler could actually have been designed to do this for you, but of course this contradicts the more general rule of "nearer" identifiers (the Derived class' 'x') hiding more remote ones (the Base class' 'x'). It seems like a reasonably nice solution would be a modifier like 'noval', maybe like this:
class Base( var x : Int ) { def increment() { x = x + 1 } }
class Derived( noval x : Int ) extends Base( x ) { override def toString = x.toString }
val derived = new Derived( 1 )
println( derived.toString ) // yields '1', as expected
derived.increment()
println( derived.toString ) // still '2', as expected
you already provided the answer that works
If the problem is that
_x
is not a nice name, then you should use a meaningful one. Alternatively, you could declare your classes as followsAnd now you would have the "nice" syntax for initializing
Derived
instances.If scala was to allow
This to me seems a very low-level detail that you don't want to deal with in the code. If this can be done safely, the compiler should do it for you.
@Gregor Scheidt your code does not work if I move toString() down to Derived, as the following:
A post from the official site said,
And Martin's reply confirms its truth:
since there is no way to prevent the compiler's action, my option is that a more robust way to reference base class field is to use a different name, for example using a underscore "_" as prefix, as the following:
The idiomatic way to avoid duplicating the field would be to write
However, in your version it looks like you actually want a second field, since you have two distinct values. As you correctly point out, giving these fields the same name is likely to lead to confusion.
Since you don't actually need the constructor argument outside of the constructor, you could use this approach (a private constructor with a companion module that acts as a factory):
You can try this:
Then
prints exactly what you want
As suggested by @jsuereth I created an enhancement ticked for Scala, just for the record, that I hope correctly summarizes the content of the discussions here. Thanks for all of your input! The ticket can be found here, the content below: https://issues.scala-lang.org/browse/SI-4762
Inadvertent shadowing of base class fields in derived classes, Warning desirable
Issue arises whenever (a) a class parameter in a derived class uses the same symbol as a field or function in a base class and (b) that base class symbol is subsequently accessed in the derived class. The derived class parameter causes the compiler to auto-generate a field of the same name that shadows the base class symbol. Any reference to that symbol in the Derived class, intended to refer to the base class field, then inadvertently (a) causes duplicate definition of the field and (b) unexpectedly (but correctly) refers to the auto-generated field in the derived class.
Code example:
Since the 'Base' class has 'val' to generate the field and the derived class does not, the developer clearly intends to use the 'Derived' 'x' only for pass-through to Base, and therefore expects the reference in 'toString' to yield the Base value. Instead, the reference in 'toString' causes the compiler to automatically generate a field 'Derived.x' that shadows the 'Base.x' field, resulting in an error. The compiler behaves correctly, but the result is a programming error.
Usage scenario (with var fields for clearer illustration of the risks):
Since this issue arises whenever anyone uses the default way to initialize base class fields from a derived class in Scala, the scenario would appear to be extremely common and result in lots of programming errors (easily fixed, but still) for newer users.
An easy work-around for this issue exists (use differing names for the derived class parameter, like '_x', 'theX', 'initialX', etc.), but this introduces unwanted extra symbols.
Solution A (Minimal): issue a warning whenever the compiler infers that a class parameter requires an auto-generated field in a derived class that would shadow a symbol already defined in a base class.
Solution B: the work-around, still required with Solution A, is to come up with a new symbol name each time one initializes a base class field. This scenario comes up all the time and polluting the namespace with workaround field names like '_x' and 'theX' seems undesirable. Instead, it might be nice to have a way to suppress automatic field generation if the developer determines that the derived class symbols would otherwise end up hiding a base class symbol (e.g., following the warning of Solution A). Maybe a useful addition to Scala would be a modifier like 'noval' (or 'passthrough' or 'temp', or whatever - in addition to 'val' and 'var'), like this:
As the base class is abstract, it doesn't look as though you really want a
val
(orvar
) in theBase
class, with the associated backing field. Instead, you're simply looking to make a guarantee that such a thing will be available in concrete subclasses.In Java, you'd use an accessor method such as
getX
to achieve this.In Scala, we can go one better, vals, vars and defs occupy the same namespace, so a
val
can be used to implement an abstractdef
(or to override a concretedef
, if that's what floats your boat). More formally, this is known as the "Uniform Access Principle"If you need for
x
to be settable via a reference toBase
, you also need to declare the "setter" (which can then be implemented with avar
):(not that I would ever encourage mutability in any design; unless there was an especially compelling justification for it. There are too many good reasons for favouring immutability by default)
UPDATE
The benefits of this approach are:
x
could be an entirely synthetic value, implemented entirely in terms of other values (i.e. the area of a circle for which you already know the radius)x
can be implemented at any arbitrary depth in the type hierarchy, and doesn't have to be explicitly passed through each intervening constructor (having a different name each time)Base
could be implemented as a trait; if you so desire