Skip to main content

The Truth and Misconceptions about Getters and Setters

· 3 min read
Haril Song
Owner, Software Engineer at 42dot

When you search for getter/setter on Google, you'll find a plethora of articles. Most of them explain the reasons for using getter/setter, often focusing on keywords like encapsulation and information hiding.

The common explanation is that by declaring field variables as private to prevent external access and only exposing them through getter/setter, encapsulation is achieved.

However, does using getter/setter truly encapsulate data?

In reality, getter/setter cannot achieve encapsulation at all. To achieve encapsulation, one should avoid using getters and setters. To understand this, it is necessary to have a clear understanding of encapsulation.

What is Encapsulation?

Encapsulation in object-oriented programming has two aspects: bundling an object's attributes (data fields) and behaviors (methods) together and hiding some of the object's implementation details internally. - Wikipedia

Encapsulation means that the external entities should not have complete knowledge of an object's internal attributes.

Why Getters and Setters Fail to Encapsulate

As we have learned, encapsulation dictates that external entities should not know the internal attributes of an object. However, getter/setter blatantly exposes the fact that a specific field exists to the outside world. Let's look at an example.

Example

public class Student {

private String name;
private int age;

public String getName() {
return name;
}

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

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String introduce() {
return String.format("My name is %s and I am %d years old.", name, age);
}
}
class StudentTest {

@Test
void student() {
Student student = new Student();
student.setName("John");
student.setAge(20);
String introduce = student.introduce();

assertThat(student.getName()).isEqualTo("John");
assertThat(student.getAge()).isEqualTo(20);
assertThat(introduce).isEqualTo("My name is John and I am 20 years old.");
}
}

From outside the Student class, it is evident that it has attributes named name and age. Can we consider this state as encapsulated?

If the age attribute were to be removed from Student, changes would need to be made everywhere getter/setter is used. This creates strong coupling.

True encapsulation means that modifications to an object's internal structure should not affect the external entities, except for the public interface.

Let's try to hide the internal implementation.

public class Student {

private String name;
private int age;

public Student(String name, int age) {
this.name = name;
this.age = age;
}

public String introduce() {
return String.format("My name is %s and I am %d years old.", name, age);
}
}
class StudentTest {

@Test
void student() {
Student student = new Student("John", 20);
String introduce = student.introduce();

assertThat(introduce).isEqualTo("My name is John and I am 20 years old.");
}
}

Now, the object does not expose its internal implementation through the public interface. It is not possible to know what data it holds, prevent it from being modified, and only communicate through messages.

Conclusion

Encapsulation is a crucial topic in object-oriented design, emphasizing designs that are not dependent on external factors. Opinions vary on the level of encapsulation, with some advocating against using both getter and setter and others suggesting that using getter is acceptable.

Personally, I believe in avoiding getter usage as much as possible, but there are situations, especially in testing, where having getters or setters can make writing test code easier. Deciding on the level of encapsulation depends on the current situation and the purpose of the code being developed.

Good design always emerges through the process of trade-offs.

info

All example codes can be found on GitHub.