8

When initialising a class, is it good practice to use the getter and setter functions in the constructors?

Or is it good practice to set the variables directly, since the constructor could be considered a kind of mutator?

rghome
  • 8,529
  • 8
  • 43
  • 62
Jarryd
  • 572
  • 4
  • 13
  • 3
    if you have some customization code in your setter, then use it, otherwise leave it simple as `this.x = x;` – Kartik Nov 13 '18 at 23:11
  • 4
    **Why** would you use getters in the constructor? That seems dangerous (what is there to get, the instance is still being constructed). Using a setter seems justifiable. – Elliott Frisch Nov 13 '18 at 23:11
  • @Kartik, except you setter could have been overridden and your customization code won't get run. – rghome Nov 14 '18 at 00:00
  • @rghome good point, but (I might be wrong) if you are overriding the setter, it will almost always mean that you are overriding the field. And in that case, the parent class constructor will be of no use. – Kartik Nov 14 '18 at 00:04
  • I think the answer to that is "not necessarily". – rghome Nov 14 '18 at 00:11

4 Answers4

16

You should not call getters and setters from the constructor.

A constructor constructs the specific class in which it is defined. It is its job to initialise the fields because - well - nothing else will.

The only way to guarantee initialising the fields is to assign them. If you call a setter there is a chance it could be overridden and it might do something else. It might call a method in a sub-class which is not initialised yet.

Calling a getter is also a bad idea if you are just getting a field from the same class. If it has been declared in the super-class you might justify it; if you need to get data from the super-class in the sub-class, you will have to call the getter (unless it is protected). If you need to communicate data from a sub-class to the super-class during construction you should pass it as a parameter. But this is a different use-case to what you are describing and the sub-class would probably not have your own field corresponding to the getter anyway.

If you have any "special" initialisation code, put that in a separate private method and call it from both the constructor and the setter separately.

rghome
  • 8,529
  • 8
  • 43
  • 62
4

It depends, do you plan on ever subclassing this class, should someone else be able to subclass your class?

  • If the answer is no then, you could use it, but I would say it's generally bad practice to do so for several reason, if you don't explicitly forbid inheritance then the class can be subclassed and the methods overridden see quote below. In general it's good practice to aim for as much immutability as you can and using getters/setters keeps you from doing that. I would also argue that Constructors should only have parameters that are necessary to initialize a class to a valid state. If they can be also passed in using setters then they might not be necessary for a valid state to be achieved.
  • If you want to design your class with inheritance in mind then the answer is no and if you use init method it can't use getters/setters either, or any method that can be overriden. Direct quote from Effective Java 2nd Edition:

There are a few more restrictions that a class must obey to allow inheritance. Constructors must not invoke overridable methods, directly or indirectly. If you violate this rule, program failure will result. The superclass constructor runs before the subclass constructor, so the overriding method in the subclass will get invoked before the subclass constructor has run. If the overriding method depends on any initialization performed by the subclass constructor, the method will not behave as expected.

KameeCoding
  • 693
  • 2
  • 9
  • 27
1

The way I see it is that I can use a setter to check the arguments and/or initialize them to some values when the constructor defines the class. I could also use a getter in a case where I have a calculated variable, but I should be very careful about the order of the statements (not recomended for error prone).

Example in JavaScript:

class Point {
  constructor (x, y) {
    this.x = x.x || x // invokes the setter
    this.y = x.y || y
  }
  toString () {
    return `The point is (${this.x}, ${this.y})` // invokes the getters
  }
  set x (newX) { // I think it should be better use 'newX' as a parameter than 'x'
    if (newX > 100) {
      console.log(`The x (${newX}) value must be < 100, `, 'x set to 0')
      this._x = 0 // if we use 'this.x' here, we will get an error (stack overflow)
      return
    }
    this._x = newX
  }
  get x () { // no one but the getter and setter should know '_x' exists
    return this._x // it has to be coherent with the setter
  }
  set y (newY) {
    if (newY > 100) {
      console.log(`The y (${newY}) value must be < 100, `, 'y set to 0')
      this._y = 0
      return
    }
    this._y = newY
  }
  get y () {
    return this._y
  }
}
fender0ne
  • 281
  • 4
  • 13
  • 1
    I think your code has errors in them. Please double check (seem to be lots of extra spaces) – Tacratis Apr 03 '19 at 22:26
  • The code was checked by ESLint without any error. I also run the code with some test and It works. Also I do not how much extra space you see, I just indented the code for readability, even though it work as intendeded. – fender0ne Apr 04 '19 at 08:38
  • @Tacratis Ohh, I did not realized that this question have a "JAVA" tag, I wrote the example in javascript. – fender0ne Apr 04 '19 at 15:13
0

No, the point of having accessors and mutators is to be able to access private fields from another class in the same package.

You technically could, but mutating variables from the constructor defeats the purpose of initializing. Accessing variables would simply be adding an extra step to getting its contents.

So yes, you should just reassign the values you want to your variables directly.