User Tools

Site Tools


java:access_specifiers_accessors_and_mutators

Access Modifiers, Accessors, and Mutators

Access modifiers

The ClickerCounter class definition we used previously encapsulates the state and behavior of a clicker counter as an object should.

ClickerCounter.java
public class ClickerCounter {
 
    int count;
 
    void click(){
        count++;
    }
 
    void reset(){
        count = 0;
    }    
}

But the definition has issues. For example, if we change our main code as follows:

ClickerExample.java
public class ClickerExample {
 
    public static void main(String[] args) {
        var myCounter = new ClickerCounter();
 
        myCounter.reset();
        myCounter.count = 492341;  // <- LOOK HERE!
        System.out.println(myCounter.count);
        myCounter.click();
        myCounter.click();
        System.out.println(myCounter.count);
    }    
}

the object will happily oblige the user's wish to set the count to some arbitrary value. That's not something you can typically do with a clicker counter and probably not what you want from your ClickerCounter object. What we are seeing here is that the object offers no protection of its state.

We can add protection of members using access modifiers. Let's look at a modified version of our class definition that gives us some protection by using two of Java's access modifiers:

ClickerCounter.java
public class ClickerCounter {
 
    private int count;
 
    public void click(){
        count++;
    }
 
    public void reset(){
        count = 0;
    }    
}

Access modifiers are placed before the start of a member definition. Java offers four levels of protection, and they just so happen to all start with the letter 'p':

  • package: this is what you get by default (i.e., when you don't specify anything, as in our first definition of the class). It means anything within the same package as the class definition is able to access the member.
  • public: anyone anywhere can access the member. This is a good choice for methods that are part of the class' interface.
  • private: only those of the same class can access the member.
  • protected: only those of the same _or a derived_ class can access the member. 1)

If we try to compile ClickerExample with the changes made above, we will see that the code will refuse to compile. When the compiler hits the statement:

myCounter.count = 492341;

it cannot access the count member of myCounter because we said it's private.

It's considered a best practice in Java programming to make all member variables private or protected except in some rare situations.

We want objects to be self-governing, and the proper use of access specifiers helps ensure this is the case. Methods can be at whatever access level is appropriate for their use. If the method is part of the class' interface, it should be public. If it's for use only internally by the object, it will typically be private or protected.

Accessors and mutators

Accessors

If we comment out the offending line above, we will see that the program still does not compile. The reason for this is in the statement:

System.out.println(myCounter.count);

The compiler is trying to access myCounter's count member, but we said that it's private so no one would clobber it. But now we can't access the value at all!

So, once you make a member variable private or protected, how do we get its value?? The answer is you can write a public method that simply returns the value of the member variable:

ClickerCounter.java
public class ClickerCounter {
 
    private int count;
 
    // NEW! accessor method
    public int getCount(){
        return count;
    }
 
    public void click(){
        count++;
    }
 
    public void reset(){
        count = 0;
    }    
}

This is an example of an accessor: a member method that provides read-only access to a value in your object's state. Accessor names typically consist of “get” followed by name of the state value (i.e., member variable) it is modifying.

We can now change our main method as follows:

public static void main(String[] args) {
    var myCounter = new ClickerCounter();
 
    myCounter.reset();  // count is 0
    System.out.println(myCounter.getCount());
    myCounter.click();
    myCounter.click();
    System.out.println(myCounter.getCount());
}

and life if good. Accessors are sometimes called getters, but I personally try to avoid that term.

Mutators

Let's now add a feature to our counter that lets the user set the maximum count: after the counter reaches the maximum, it should reset. Just like a physical clicker counter that only counts up to, say, 9,999.

We'll need another member variable to store this upper bound, and we'll have to modify the click() method to check it.

private int maxCount; // new member variable
 
...
 
// modified click() method
public void click(){
    if (count < maxCount) {
        count++;
    } else {
        count = 0;
    }
}

Following best-practices, I made maxCount private. But now how do we set it? The answer is we write a public method that sets the value. Even better, the public method can check to make sure the value it's being set to is value. Such a method is called a mutator: an instance method that mutate the value of a state value. Mutator names typically consist of “set” followed by with the name of the state value (member variable) being modified.

// NEW! mutator for maxCount
public void setMaxCount(int newMax) {
    if (newMax > 0) {
        maxCount = newMax;
    } else {
        System.out.println("Invalid new maximum count: " + newMax);
    }
}

Mutators should checks that new values are valid. This is another example of what's needed to make an object self-governing.

Mutators are sometimes called setters, but I personally try to avoid that term.

Putting it all together, we now have:

ClickerCounter
/**
 * Encapsulate a tally counter/clicker-counter
 */
public class ClickerCounter {
 
    // Member variables
    private int count;
    private int maxCount;
 
    // Accessors and mutators
    public int getCount() {
        return count;
    }
 
    public void setMaxCount(int newMax) {
        if (newMax > 0) {
            maxCount = newMax;
        } else {
            System.out.println("Invalid maximum count: " + newMax);
        }
    }
 
    // Interface methods
    public void click() {
        if (count < maxCount) {
            count++;
        } else {
            count = 0;
        }
    }
 
    public void reset() {
        count = 0;
    }
}
ClickerExample.java
public class ClickerExample {
 
    public static void main(String[] args) {
        var myCounter = new ClickerCounter();    // instantiate a ClickerCounter
 
        myCounter.setMaxCount(100);
        myCounter.reset();  // count is 0
        for (int i = 0; i < 99; i++){
            myCounter.click();
        }
        System.out.println(myCounter.getCount());  // 99
        myCounter.click();
        System.out.println(myCounter.getCount());  // 100
        myCounter.click();
        System.out.println(myCounter.getCount());  // 0
    }    
}

Many new programmers feel the need to write both accessors and mutators for all instance variables. Don't. To reduce the potential for introducing bugs, you should only write methods that will actually be used. If you don't need a accessor or mutator, don't write it.

this

Every class has an automatically generated this member variable that points to the object it's associated with. In other words, it's a reference back to the object itself.

this can be used to simplify writing mutators and other methods that have parameters because it eliminates the need to come up with new names for parameters. Let's see how this works by modifying the setMaxCount() method.

public void setMaxCount(int maxCount) {
    if (maxCount > 0) {
        this.maxCount = maxCount;
    } else {
        System.out.println("Invalid maximum count: " + maxCount);
    }
}

We have changed the name of the parameter to maxCount. Inside the method it shadows the member variable maxCount, but we can still access the member variable through the this reference. In essence, what we are saying in the above is, “Set this object's maxCount to the maxCount we passed in.”

this will be used in other ways as well, so get comfortable with it. Think of it as meaning, “this object”.

Copyright © 2020 Mithat Konar. All rights reserved.

1)
The difference between private and protected won't be clear until you've studied class inheritance.
java/access_specifiers_accessors_and_mutators.txt · Last modified: 2020/09/12 02:01 by mithat