If "safer" is supposed to refer to security, then it's not really true. There are many ways to access an object's private variables.
If "safer" is meant to refer to robust code, meaning code that is less likely to hit exceptions or otherwise fail to work properly, then yes, it is safer. The benefit is that your class has total control over the value.
The simplest example is null values. Consider this class:
public class Person {
public String firstName;
public String lastName;
public List<Person> relatives;
}
If I want to write a method that accepts a Person instance, I have no guarantee that the fields are not null. I have to clutter my code with checks:
void populateFieldsWith(Person person) {
firstNameField.setText(person.firstName != null ? person.firstName : "");
lastNameField.setText(person.lastName != null ? person.lastName : "");
if (person.relatives != null) {
List<String> names = new ArrayList<>();
for (Person relative : person.relatives) {
if (relative != null
&& relative.firstName != null
&& relative.lastName != null) {
names.add(relative.firstName + " " + relative.lastName);
}
}
nameList.setData(names);
} else {
nameList.setData(Collections.emptyList());
}
}
If the fields are private, the Person class is the only class who can modify them, which gives the Person class the power to ensure they are non-null:
public class Person {
private String firstName;
private String lastName;
private final List<Person> relatives = new ArrayList<>();
/**
* Creates a new Person instance.
*
* @param firstName person's first name; cannot be null
* @param lastName person's last name; cannot be null
*
* @throws RuntimeException if any argument is null
*/
public Person(String firstName,
String lastName) {
setFirstName(firstName);
setLastName(lastName);
}
/**
* Returns this person's first name. This never returns null.
*/
public String getFirstName() {
return firstName;
}
/**
* Sets this person's first name.
*
* @param name new non-null first name
*
* @throws RuntimeException if any argument is null
*/
public void setFirstName(String name) {
Objects.requireNonNull(name, "Name cannot be null");
this.firstName = name;
}
/**
* Returns this person's last name. This never returns null.
*/
public String getLastName() {
return lastName;
}
/**
* Sets this person's last name.
*
* @param name new non-null last name
*
* @throws RuntimeException if any argument is null
*/
public void setLastName(String name) {
Objects.requireNonNull(name, "Name cannot be null");
this.lastName = name;
}
/**
* Returns a list of this person's relatives. This never returns null
* but it may return an empty list.
*/
public List<Person> getRelatives() {
return new ArrayList<Person>(relatives);
}
public void setRelatives(List<Person> relatives) {
Objects.requireNonNull(relatives, "List cannot be null");
for (Person person : relatives) {
Objects.requireNonNull(person, "List cannot contain null");
}
this.relatives.clear();
this.relatives.addAll(relatives);
}
public void addRelative(Person relative) {
Objects.requireNonNull(relative, "Person cannot be null");
this.relatives.add(relative);
}
}
Now we have a class that is unbreakable (unless someone uses reflection to get around the private access, but in that case you're dealing with malicious code and that enters the entirely different subject of runtime security).
There is no way firstName
can be null, ever, because the only way any outside code can set it is to go through the setFirstName
method, and that method will not allow the field to be set to a null value.
Now, let's return to the code that wanted to make use of a Person instance:
void populateFieldsWith(Person person) {
firstNameField.setText(person.getFirstName());
lastNameField.setText(person.getLastName());
List<String> names = new ArrayList<>();
for (Person relative : person.getRelatives()) {
names.add(relative.getFirstName() + " " + relative.getLastName());
}
nameList.setData(names);
}
The method no longer needs to be cluttered with checks for null. Programmers can safely assume that the value is not null, because the javadoc guarantees it. The guarantee is an honest one because there is no code path that can cause the method to return null.
Null values is just one use. The same setFirstName
method could also check that the string is non-empty, check that it doesn't exceed a maximum length, check that it only contains certain types of characters, and so on, all so the getFirstName
method can provide a guarantee via its javadoc to other programmers that it will return valid data.