SCJP Study Guide:
OO Concepts


Printer-friendly version Printer-friendly version | Send this 
article to a friend Mail this to a friend


Previous Next vertical dots separating previous/next from contents/index/pdf Contents
Encapsulation XyzWs SCJP Study Guide: Encapsulation

Encapsulation


Encapsulation is one of the core fundamental guiding principles of object oriented programming. Encapsulation is the principal of keeping the internal details of a class state and behaviors hidden from the other classes that use it.

In Java, encapsulation is implemented by a class, a class is the generic form of an object. There are attributes and operations in a class. The attributes are the properties of the class. Different values of the attributes describes different states of an object. The operations are the interface that the other objects shall use to change the states of the object. Both the attributes and the operations, i.e., methods, can be public or private. A private attributes or method can only be accessed by code within the implementation of the class. While a public attribute or method can be accessed from any class.

Encapsulation is about data hiding. Data hiding means access to all of an object's attributes is tightly controlled. This is accomplished by making all the attributes private. And then to provide both a getter and a setter to each of the private attribute. The getters and the setters are made public. So that the getters and setters are the only interface to access to the attributes. This is a key part of encapsulation.

For example, the BankAccount class:

public class BankAccount {
  private float balance;
  public float getBalance();
  public void setBalance(float b) {
    this.balance = b;
  }
}

Making the balance attribute to be private disallows other objects to access or change the balance directly. Instead, they have to use the getBalance() and the setBalance() methods. This way you hide the classes' inner details from the outside world. Moreover, it protects your objects from improper use by forcing all use to go through the objects' interfaces.

If we make the attribute to public, all the other objects can directly change it's value by simply calling:

...
public class BankAccount {
  public float balance;
...
  public static void main(String [] args) {
    BankAccount myAccount = new BankAccount();
	myAccount.balance = -100.00; // lost control of how the balance should be set
}

Instead, by following the encapsulation guideline, you should make the balance attribute as private, and can prevent setting the balance to a negative number by doing this:

...
public void setBalance(float b) {
  if (b < 0 ){
    System.out.println("negative balance is not allowed.");
    return;
  } else {
    this.balance = b;
  }
}
...

This shows another advantage encapsulation. That is it allows you to change the implementation details where necessary, without breaking compatibility. The interconnections between pieces of code is called 'coupling', and minimizing it makes for more reusable and maintainable classes. The expected behavior of a class or method is referred to as its 'contract'. You do not want to expose any more than the minimum required to support the contract, or you can end up with tangled interdependent code with poor maintainability, and in all likelihood, poor quality.

In the above example, you can keep adding ad hoc restrictions to the balance without requiring the objects that uses the Bank Account to change. For instance, a new requirement came in that the minimum balance has to be 1000.00, then you can only put this in the setBalance() method, while the objects using the setBalance() method don't need to be changed.

...
public void setBalance(float b) {
  if (b < 1000.00 ){
    System.out.println("minimum balance has to be 1000.");
    return;
  } else {
    this.balance = b;
  }
}
...

As you can see, by using a setter, we can control access to the attribute and also do the error checking in a single place, thus protects objects that use the encapsulated object. If not for encapsulation, the users of your object could become dependent upon the object's implementation. This tight coupling between objects means that the users of your object would break each time you needed to rework the implementation.

Encapsulation also aids polymorphism and inheritance - if the internal details of a class are exposed and used, it is very hard to substitute a different implementation of the same contract.

To achieve good encapsulation, make everything have the tightest possible access control so if something should not be used in a given context, limit the access to prohibit it. In particular, class instance variables should be private, and accessed only through 'get'/'set' methods (a.k.a accessors and mutators). This would allow you to do some work or checking before reading/writing a value to the variable. You could remove the variable entirely and derive it or obtain it from another source. If the variable was public, and was accessed directly, should changes would break client code.

Static classes are good examples of encapsulation.

Let's look at a class that has only static methods:

public class MessageLogger {

  private static boolean debugOn;
  public static void setDebug( boolean debug ) {
	debugOn = debug;
  }
  public static void debug( String message ) {
    if( debugOn ) { 
      System.out.println( "DEBUG: " + message );
    }
  }
  public static void error( String message ) {
    System.out.println( "ERROR: " + message );
  }
}

The MessageLogger logs the message in the program. The debug messages are only logged when the debugOn is set to true. The MessageLogger keeps an internally encapsulated state: debugOn. All interaction with MessageLogger must be done through the public method interface.


Previous Next vertical dots separating previous/next from contents/index/pdf Contents

  |   |