Encapsulation is one of the fundamental principles of Object-Oriented Programming (OOP). It refers to the bundling of data (attributes) and methods (functions) that operate on the data into a single unit, typically a class. Encapsulation also involves restricting direct access to some of an object's components, which is a means of preventing unintended interference and misuse of the data.
Key Concepts of Encapsulation
- Data Hiding: Encapsulation allows the internal state of an object to be hidden from the outside. This is typically achieved using access modifiers.
- Access Modifiers: These are keywords used to set the accessibility of classes, methods, and other members. Common access modifiers in Groovy include:
private
: The member is accessible only within the class.protected
: The member is accessible within the class and by subclasses.public
: The member is accessible from any other class.
- Getters and Setters: Methods that provide controlled access to the attributes of a class. Getters retrieve the value of an attribute, while setters modify the value.
Practical Example
Let's create a simple class Person
to demonstrate encapsulation in Groovy.
class Person { private String name private int age // Getter for name String getName() { return name } // Setter for name void setName(String name) { this.name = name } // Getter for age int getAge() { return age } // Setter for age void setAge(int age) { if (age > 0) { this.age = age } else { println "Age must be positive." } } } // Using the Person class def person = new Person() person.setName("John Doe") person.setAge(30) println "Name: ${person.getName()}" println "Age: ${person.getAge()}"
Explanation
- Private Attributes: The
name
andage
attributes are declared asprivate
, meaning they cannot be accessed directly from outside the class. - Getters and Setters: The
getName
,setName
,getAge
, andsetAge
methods provide controlled access to thename
andage
attributes. - Validation: The
setAge
method includes a validation check to ensure the age is positive.
Practical Exercises
Exercise 1: Create a BankAccount Class
Create a BankAccount
class with the following attributes and methods:
private double balance
public double getBalance()
public void deposit(double amount)
public void withdraw(double amount)
Ensure that the withdraw
method does not allow the balance to go negative.
Solution
class BankAccount { private double balance = 0.0 double getBalance() { return balance } void deposit(double amount) { if (amount > 0) { balance += amount } else { println "Deposit amount must be positive." } } void withdraw(double amount) { if (amount > 0 && amount <= balance) { balance -= amount } else { println "Invalid withdraw amount." } } } // Using the BankAccount class def account = new BankAccount() account.deposit(100.0) account.withdraw(30.0) println "Balance: ${account.getBalance()}"
Exercise 2: Create a Student Class
Create a Student
class with the following attributes and methods:
private String studentId
private String name
private double gpa
public String getStudentId()
public void setStudentId(String studentId)
public String getName()
public void setName(String name)
public double getGpa()
public void setGpa(double gpa)
Ensure that the setGpa
method only accepts values between 0.0 and 4.0.
Solution
class Student { private String studentId private String name private double gpa String getStudentId() { return studentId } void setStudentId(String studentId) { this.studentId = studentId } String getName() { return name } void setName(String name) { this.name = name } double getGpa() { return gpa } void setGpa(double gpa) { if (gpa >= 0.0 && gpa <= 4.0) { this.gpa = gpa } else { println "GPA must be between 0.0 and 4.0." } } } // Using the Student class def student = new Student() student.setStudentId("S12345") student.setName("Alice") student.setGpa(3.8) println "Student ID: ${student.getStudentId()}" println "Name: ${student.getName()}" println "GPA: ${student.getGpa()}"
Common Mistakes and Tips
- Direct Access: Avoid accessing private attributes directly from outside the class. Always use getters and setters.
- Validation: Always include validation in setters to ensure the integrity of the data.
- Encapsulation Overhead: While encapsulation adds a layer of protection, it can also introduce some overhead. Use it judiciously to balance between protection and performance.
Conclusion
Encapsulation is a powerful concept in OOP that helps in protecting the internal state of an object and provides controlled access to its attributes. By using access modifiers and getter/setter methods, you can ensure that your classes are robust and maintainable. In the next topic, we will explore more advanced OOP concepts such as inheritance.