Setter methods or constructors

I think you asked a good question: - But when do we use constructors and when do we use setters?

First, let's start with some concepts. I hope this explanation helps everybody that want to know when to use constructors or setters() and getters() methods (Accessors and Mutators). Constructors are similar to Methods, however there are few differences between constructor and method in java:

1) Constructor is used to initialize the state of an object. Method is used to expose behaviour of an object.

2) Constructor must not have return type. Method must have return type.

3) Constructor is invoked implicitly. Method is invoked explicitly.

4) Getters(), or accessors, are methods that provide access to an object's instance variables. Setters(), or mutators, are methods that provide the caller with an opportunity to update the value of a particular instance variable.

Having this clear, let's think in term of Object Oriented Programming (OOP), to meet the requirement of OOP principals (The Objects Oriented Programming (OOP) is constructed over four major principles: Encapsulation, Data Abstraction, Polymorphism and Inheritance.), Getter() and Setter() methods is the key to achieve this.

Here is a formula that is going to show you what I mean:

Private fields + Public accessors == Encapsulation;

As you can see based on this formula when we set private fields and use public accesors we are performing encapsulation one of the 4 OOP principals.

Here I am going to provide you two classes, I added comment on it to try to make my code self explaining. Take these classes as a lab on methods with Customer and TestCustomer [the one with the main() method] classes, you can copy the code and run it yourself. Note I used two constructors in order to explain a class having multiple constructors and having public setters() and getters() methods in order to access private instance variables:

package com.exercise.lecture2;

/**
 * 1) Create a Customer class that has the following attributes:
 * name, SSN.
 * 2) This class should have two methods: getName() and getSSN().
 * 3) If the class is instantiated with only a SSN, then give the default name of "John Doe". (HINT: Use two constructors)
 * 4) Also, add a method toString(), that returns a string representation of the customer object (name and SSN concatenated).
 *      Make sure to set this method public.
 * 5) Create a class to test your program (e.g. a class that include the main() method). In your test program, instantiate
 *      three customers and print out the value using toString() method.
 * 
 * @author Samuel M.
 *
 */

//this class is complemented with class  TestLabCustomer.java
public class LabCustomer {

// Private filds: name and socialSecurityNum
    private String name;
    private int socialSecurityNum;

    // constructors
    public LabCustomer(String name, int socialSecurityNum) {
        this.name = name;
        this.socialSecurityNum = socialSecurityNum;
    }

    /** The keyword 'this' can be used to call a constructor from a constructor,
     * when writing several constructor for a class, there are times when
     * you'd like to call one constructor from another to avoid duplicate code.
     */
    // Account with This() on a second constructor
    public LabCustomer(int socialSecurityNum) {
        this("John Doe", socialSecurityNum); // default name is printed if only the SSN is provided
    }

    // Public accessors (getters and setters)
    String getName() {
        return name;
    }

    void setName(String name) {
        this.name = name;
    }

    int getSSN() {
        return socialSecurityNum;
    }

    void setSSN(int socialSecurityNum) {
        this.socialSecurityNum = socialSecurityNum;
    }


    // instance method
    public String toString() { //overriding the toString() method 
        return ("Customer name: " + getName() + ", SSN#: " + getSSN() ); // concatenating the name and SSN

    }
}

Here is the test class, the one that has the main() method and calling an instance method after instantiating an object of the previews class:

package com.exercise.lecture2;

//this class is complemented with class  LabCustomer.java
public class TestLabCustomer {

    public static void main(String[] args) {
        // Instantiating an object of class LabCustomer and creating three customers objects
        LabCustomer cust1 = new LabCustomer("Juan Melendez", 123457789);
        LabCustomer cust2 = new LabCustomer("Mary Lee", 125997536);
        LabCustomer cust3 = new LabCustomer(124963574); // when instantiating with no "name", the default (John Doe) is printed 

        /**
         * Once you've instantiated an object and have an object variable,
         * you can use object variable to call an instance method. 
         *  e.g.:
         *  object variables: cust1, cust2, cust3
         *  call the method toString() using the object variable and dot [.] in order to perform the method call.
         */
        // calling method toString() in class LabCustomer to print customer values
        System.out.println(cust1.toString());
        System.out.println(cust2.toString());
        System.out.println(cust3.toString());

    }

}

result:

Customer name: Juan Melendez, SSN#: 123457789

Customer name: Mary Lee, SSN#: 125997536

Customer name: John Doe, SSN#: 124963574


You should use the constructor approach, when you want to create a new instance of the object, with the values already populated(a ready to use object with value populated). This way you need not explicitly call the setter methods for each field in the object to populate them.

You set the value using a setter approach, when you want to change the value of a field, after the object has been created.

For example:-

MyObject obj1 = new MyObject("setSomeStringInMyObject"); // Constructor approach
// Yippy, I can just use my obj1, as the values are already populated
// But even after this I can change the value
obj1.setSomeString("IWantANewValue"); // Value changed using setter, if required.
..
MyObject obj2 = new MyObject();
obj2.setSomeString("setSomeStringNow"); // Setter approach
// values weren't populated - I had to do that. Sad :(

And as Axel mentioned, if you want to create immutable objects, you cannot use setter-methods approach. I won't say everything has to be initialised in the constructor because different approaches exist, like lazy-evaluation which can be used even with immutable objects.