How to Use Identical Names for Fields and Constructor Parameters

Can I use identical names for fields and constructor parameters?

Yes it is legal and works on all platforms.
It will correctly initialize your member variable a, to the passed in value a.

It is considered by some more clean to name them differently though, but not all. I personally actually use it a lot :)

Initialization lists with the same variable name works because the syntax of an initialization item in an initialization list is as follows:

<member>(<value>)

You can verify what I wrote above by creating a simple program that does this: (It will not compile)

class  A
{

A(int a)
: a(5)//<--- try to initialize a non member variable to 5
{
}
};

You will get a compiling error something like: A does not have a field named 'a'.


On a side note:

One reason why you may not want to use the same member name as parameter name is that you would be more prone to the following:

class  A
{

A(int myVarriable)
: myVariable(myVariable)//<--- Bug, there was a typo in the parameter name, myVariable will never be initialized properly
{
}
int myVariable;
};

On a side note(2):

One reason why you may want to use the same member name as parameter name is that you would be less prone to the following:

class  A
{

A(int myVariable_)
{
//<-- do something with _myVariable, oops _myVariable wasn't initialized yet
...
_myVariable = myVariable_;
}
int _myVariable;
};

This could also happen with large initialization lists and you use _myVariable before initializing it in the initialization list.

Disambiguate constructor parameter with same name as class field of superclass

@MarioGalic answered this question. I'll just make some additions that are too long for comments.

Common misunderstanding is that i in

class MyClass(i : Int)

is just a constructor parameter and not a field. Actually if we do

import scala.reflect.runtime.universe._
println(reify{
class MyClass(i : Int)
}.tree)

we'll see (Scala 2.13.2)

{
class MyClass extends AnyRef {
<paramaccessor> private[this] val i: Int = _;
def <init>(i: Int) = {
super.<init>();
()
}
};
()
}

So a parameter of primary constructor without val/var generates private[this] field. So

class MyClass(i : Int)

is similar to

class MyClass(private[this] val i : Int)

and NOT similar to Java's

public class MyClass { 
public MyClass(int i) {
}
}

without fields.

We can check that i is a field referring to it with this inside class body

class MyClass(i : Int) {
println(this.i)
}
new MyClass(1) // prints 1

The field i is private[this] so we can't refer it outside the class body (or inside the body on instance different than this)

class MyClass(i : Int) {
//println(new MyClass(2).i) //doesn't compile
}
//new MyClass(1).i //doesn't compile

I didn't find proper place in Scala specification but such behavior is well-known for a long time. For example in "Scala for the impatient" by Cay S. Horstmann it's written (edition 2, section 5.7):

Construction parameters can also be regular method parameters, without val or var. How these parameters are processed depends on their usage inside the class.

  • If a parameter without val or var is used inside at least one method, it becomes a field. For example,

     class Person(name: String, age: Int) {  
    def description = name + " is " + age + " years old"
    }

    declares and initializes immutable fields name and age that are object-private. Such a field is the equivalent of a private[this] val field (see Section 5.4,“Object-Private Fields,” on page 56).

  • Otherwise, the parameter is not saved as a field. It’s just a regular parameter that can be accessed in the code of the primary constructor. (Strictly speaking, this is an implementation-specific optimization.)

Actually in 2.13.2 I can't confirm the second case.


Now let's have two classes.

Scala doesn't allow

class Superclass {
val i: Int = 1
}

class Subclass extends Superclass {
//val i: Int = 2 //doesn't compile
}

unless we add override

class Superclass {
val i: Int = 1
}

class Subclass extends Superclass {
override val i: Int = 2
}

But if Superclass's field is private[this] everything is ok without override

class Superclass {
private[this] val i: Int = 1
}

class Subclass extends Superclass {
val i: Int = 2
}

Actually if we try to add override this will not compile.

The reason is this being not overriding. One of fields is private[this] i.e. not accessible outside the object where the field is defined in, so these are just two different fields:

class Superclass {
private[this] val i: Int = 1
}

class Subclass extends Superclass {
val i: Int = 2

println(this.i) // or just println(i)
// println((this: Superclass).i) //doesn't compile
}

new Subclass
//2

or

class Superclass {
val i: Int = 1
}

class Subclass extends Superclass {
private[this] val i: Int = 2

println(this.i) // or just println(i)
println((this: Superclass).i)
}

new Subclass
//2
//1

So in our case

class Superclass(var i : Int)

class Subclass(i : Int) extends Superclass(0)

are like

class Superclass extends AnyRef {
var i: Int = _
def this(_i: Int) = {
super() //pseudocode
i = _i
}
}

class Subclass extends Superclass {
private[this] val i: Int = _ //pseudocode
def this(_i: Int) = {
super(0) //pseudocode
i = _i //pseudocode because "i" is a val -- well, compiler can do things that we can't do in source code
}
}

Inside Subclass this.i or just i refers to Subclass's field private[this] val i: Int and (this: Superclass).i refers to Superclass's field var i: Int.


Do scala constructor parameters default to private val?

Scala Constructor Parameters

https://www.scala-lang.org/old/node/8384.html

Constructor parameters with same name as atributes

In Scala the parameters of the constructor become public attributes of the class if declared as a var or val.

scala> class Point(val x: Int, val y: Int){}
defined class Point

scala> val point = new Point(1,1)
point: Point = Point@1bd53074

scala> point.x
res0: Int = 1

scala> point.y
res1: Int = 1

Edit to answer the question in comments "if they were private fields, shouldn't my first code snipped after the edit have worked?"

The constructor class Point(x: Int, y: Int) generates object-private fields which only allow methods of the Point class to access the fields x and y not other objects of type Point. that in the + method is another object and is not allowed access with this definition. To see this in action define add a method def xy:Int = x + y which does not generate a compile error.

To have x and y accessible to the class use a class-private field which is as follows:

class Point(private val x: Int, private val y: Int) {
def +(that: Point): Point = new Point(this.x + that.x, this.y + that.y)
}

Now they are not accessible outside of the class:

scala> val point = new Point(1,1)
point: Point = Point@43ba9cea

scala> point.x
<console>:10: error: value x in class Point cannot be accessed in Point
point.x
^
scala> point.y
<console>:10: error: value y in class Point cannot be accessed in Point
point.y

You can see this in action by using scalac -Xprint:parser Point.scala.

Common practice for variables with similar names in constructor parameter and as a property

public MyClass(int index)
{
this.index = index;
}

This is at least the common way of doing in Java, and I guess it's the same in C#. It makes the name more meaningful than i, and uses the same name as the property to designate the same thing.

Initializing member variables using the same name for constructor arguments as for the member variables allowed by the C++ standard?

I wonder what the C++ standard says about it? Is it legal and guaranteed to always work?

Yes. That is perfectly legal. Fully Standard conformant.

Blah(std::vector<int> vec): vec(vec){}
^ ^
| |
| this is the argument to the constructor
this is your member data

Since you asked for the reference in the Standard, here it is, with an example.

§12.6.2/7

Names in the expression-list of a mem-initializer are evaluated in the scope of the constructor for which the mem-initializer is specified.

[Example:
class X {
int a;
int b;
int i;
int j;
public:
const int& r;
X(int i): r(a), b(i), i(i), j(this->i) {}
//^^^^ note this (added by Nawaz)
};

initializes X::r to refer to X::a,
initializes X::b with the value of the
constructor parameter i, initializes
X::i with the value of the constructor
parameter i, and initializes X::j with
the value of X::i; this takes place
each time an object of class X is
created. ]

[Note: because the
mem-initializer are evaluated in the
scope of the constructor, the this
pointer can be used in the
expression-list of a mem-initializer
to refer to the object being
initialized. ]

As you can see, there're other interesting thing to note in the above example, and the commentary from the Standard itself.


BTW, as side note, why don't you accept the parameter as const reference:

 Blah(const std::vector<int> & vec): vec(vec) {}
^^^^const ^reference

It avoids unneccessary copy of the original vector object.

How do I use the same variable name of a local variable and a constructor parameter in the same class?

Your code should be

public class StringTester{
private String str1;
private String str2;

StringTester(String str1, String str2)
{
this.str1 = str1;
this.str2 = str2;
}
}

Constructor parameters with same name as atributes

In Scala the parameters of the constructor become public attributes of the class if declared as a var or val.

scala> class Point(val x: Int, val y: Int){}
defined class Point

scala> val point = new Point(1,1)
point: Point = Point@1bd53074

scala> point.x
res0: Int = 1

scala> point.y
res1: Int = 1

Edit to answer the question in comments "if they were private fields, shouldn't my first code snipped after the edit have worked?"

The constructor class Point(x: Int, y: Int) generates object-private fields which only allow methods of the Point class to access the fields x and y not other objects of type Point. that in the + method is another object and is not allowed access with this definition. To see this in action define add a method def xy:Int = x + y which does not generate a compile error.

To have x and y accessible to the class use a class-private field which is as follows:

class Point(private val x: Int, private val y: Int) {
def +(that: Point): Point = new Point(this.x + that.x, this.y + that.y)
}

Now they are not accessible outside of the class:

scala> val point = new Point(1,1)
point: Point = Point@43ba9cea

scala> point.x
<console>:10: error: value x in class Point cannot be accessed in Point
point.x
^
scala> point.y
<console>:10: error: value y in class Point cannot be accessed in Point
point.y

You can see this in action by using scalac -Xprint:parser Point.scala.



Related Topics



Leave a reply



Submit