Chapter 6

Packages, Interfaces, and Encapsulation

If you want to find out if a particular programming language is object-oriented, ask if it supports inheritance, encapsulation, and polymorphism. You know by now that Java supports inheritance, which lets you design a class by deriving it from an existing one. This feature allows you to reuse existing code without copy-pasting reusable code fragments from other classes — the class NJTax from Listing 3-5 was designed this way.

In Lessons 6 and 7 you learn what encapsulation means and continue studying coding techniques and best practices. While this lesson will show you several short code examples illustrating certain programming topics, the next one will bring all the pieces you’ve learned so far together in one larger practical application.

Java Packages

A decently size project can have hundreds of Java classes and you need to organize them in packages (think file directories). This will allow you to categorize files, control access to your classes (see the section “Access Levels” later in this chapter), and avoid potential naming conflicts: If both you and your colleague coincidentally decide to call a class Util, this won’t be a problem as long as these classes are located in different packages.

Sometimes you’ll be using third-party libraries of Java classes written in a different department or even outside your firm. To minimize the chances that package names will be the same, it’s common to use so-called reverse domain name conventions. For example, if you work for a company called Acme, which has the website acme.com, you can prefix all package names with com.acme. To place a Java class in a certain package, add the package statement at the beginning of the class (it must be the first non-comment statement in the class). For example, if the class Tax has been developed for the Accounting department, you can declare it as follows:

package com.acme.accounting;
class Tax {
  // the class body goes here
}

If you declare the class Tax as shown in the preceding code, the file Tax.java must be stored in the corresponding directory tree:

  Com
       Acme
          Accounting

Java classes are also organized into packages and the fully qualified name of a class consists of the package name followed by the class name. For example, the full name of the Java class Double is java.lang.Double, where java.lang is the package name. As a matter of fact, java.lang is the only package name that you don’t have to explicitly mention in your code in order for its classes to be found, unless all classes are located in the same package.

The program documentation on all Java 6 packages is available at the following URL: http://download.oracle.com/javase/6/docs/api/.

Let’s say your Tax class needs to connect to some URL on the Net with the help of the class URL located in the java.net package. You can write code containing the fully qualified name of this second class:

java.net.URL myURL = new java.net.URL ("http://www.acme.com");

But instead of using this rather long notation, include the import statement right above the class declaration, and then use just the name of the class:

import java.net.URL;
class Tax{
   URL myURL = new URL("http://www.acme.com");
   ... 
}

If you need to import several classes from the same package, use the wild card in the import statement rather then listing each of the classes on a separate line:

import java.net.*;

Encapsulation

Encapsulation is the ability to hide and protect data stored in Java objects. You may ask, “Who are the bad guys who want to illegally access my data?” It’s not about bad guys. When a developer creates a Java class, he or she plans for a certain use pattern of this code by other classes. For example, the variable grossIncome should not be modified directly, but via a method that performs some validation procedures to ensure that the value to be assigned meets application-specific rules.

A Java developer may decide to “hide” 15 out of 20 variables, say, so other classes can’t access them. Imagine how many parts exist in a car and how many functions those parts can perform. Does the driver need to know about all of them? Of course not. The driver needs to know how to start and stop the car, how to indicate turns, open the windows, turn on the wipers, and do a few dozen other simple operations, which in programming jargon can be called the car’s public interface.

Access Levels

Java classes, methods, and member variables can have public, private, protected, and package access levels, for example:

public class Tax {
   private double grossIncome; 
   private String state;
   private int dependents;
   protected double calcTax(...) {...}
}

The keyword public means that this element (a class, a method, or a variable) can be accessed from any other Java class. The keyword protected makes the element “visible” not only from the current class, but from its subclasses too.

The keyword private is the most restrictive one, as it makes a member variable or a method accessible only inside this class. For example, our class Tax may need some additional internal methods that could be called from the method calcTax(). The users of the class Tax do not need to know about these methods and they should be declared as private.

If you do not specify any access level, the default is package, which means that only classes located in the same package will have access to this method or variable. Java classes should expose only the methods that outsiders have to know, such as calcTax().

If you are not sure which access level to give to methods or variables, just make them all private; if later on during development some other class needs to access them, you can always change the access level to be less restrictive. This will protect all the internals of your application from misuse. Think of it this way: “I want to sell my class Tax to various accounting firms across the country. If their software developers integrate my class with their existing systems, what are the methods that they must know about to be able to calculate tax?” If car designers did not ask themselves similar questions, drivers would need to press dozens of buttons just to start the engine.

The Keyword final

The keyword final can have different meanings depending on the context. It’s explained in the next sections.

final Variables

You can use the keyword final while declaring variables, methods, and classes. A final variable becomes a constant (see Lesson 3) that can be initialized only once and can’t change its value during the run time. Some people may argue that a constant and a variable that can get initialized only once are not the same thing, but the fact that you can’t change their values makes them very similar.

Even though you can declare constants inside a method, it’s more common to declare them on the class level so they can be reused by several methods of the same class. You can also declare a variable with the keyword static and with a proper access method so they can be used:

static final int BOILING_TEMP = 212; //in Fahrenheit

final Methods

If you declare a class method with a final keyword, this method can’t be overridden if someone decides to extend the class. Today it may seem obvious to you that a particular method will never ever need to be overridden. What are the chances that the formula to convert Fahrenheit to Celsius will be changed any time soon?

static final double convertToCelsius(double far){
           return ((far - 32) * 5 / 9);
}

But in many cases developers create reusable libraries of Java classes, finalizing functionality that’s not written in stone. While it may seem to you that a particular method will never need to be overridden, you might have predicted all use patterns of this class. If this happens, some other developer will have to jump through hoops to create another version of your method in a subclass.

Many years ago the Java compiler optimized (inlined) final methods. It doesn’t do that anymore — all methods are optimized by the Hotspot JVM. Just think twice before making a method final. 

final Classes

If you declare a class as final, no one will be able to subclass it. For example, Java’s class String has been created as immutable and therefore declared as final (see http://java.sun.com/javase/6/docs/api/java/lang/String.html). If a class is declared as final, all its methods become implicitly final.

Interfaces

There are different approaches to how to start designing a class. Some people think of the behavior it should support. The behavior is implemented in a class in the form of methods, which can be declared in a separate entity called an interface. Then have your class implement this interface. Listing 6-1 shows an interface with one method declaration.

In general, you should have a reason for declaring the methods separately from the class that will implement it, and you’ll see these reasons in the next lesson when you learn about polymorphism, For now, let’s just get familiar with the syntax of defining and using interfaces. The interface will not have any implementations — just declarations. (There is an exception: Marker interfaces don’t even have declarations. The next section is devoted to this type of interface.)

When a class declares that it implements a certain interface, it guarantees that it contains implementation for all methods from this interface. And a class can implement more than one interface: just separate their names with commas.

Let’s say you have two types of workers in your organization, employees and contractors, and that you create the classes Employee and Contractor to implement functionalities that reflect specifics of these different groups. Each person is entitled to a pay raise, though for employees this means a salary increase and for contractors it’s an increase of an hourly or daily rate.

Instead of creating two different methods in these classes, it’s better to define an interface called, say, Payable that contains the declaration of the method increasePay(), and to have both classes implement it, as in Listings 6-1, 6-2, and 6-3. Every method declared in the interface automatically becomes public.

download.eps

Listing 6-1: Payable interface

public interface Payable {
       boolean increasePay(int percent);
}
download.eps

Listing 6-2: Class Employee

public class Employee implements Payable {
 
       public boolean increasePay(int percent) {
              // implement salary raise here
       }
 
}
download.eps

Listing 6-3: Class Contractor

public class Contractor implements Payable {
       public boolean increasePay(int percent) {
              // implement hourly rate increase here
       }
}

Since both Employee and Contractor contain the clause implements Payable, you must implement the increasePay() method in each of the classes, or your code won’t compile. Creating classes with common interfaces leads to cleaner design of your application and makes the code more readable. But what’s more important is that with the help of interfaces you can introduce polymorphic behavior to your program, and this will be illustrated in Lesson 7.

Besides method signatures, Java interfaces can contain declarations of final variables. For example, you can create a final variable in the Payable interface for the maximum percentage of pay increase (all variables declared in the interface automatically become public static final).

int INCREASE_CAP = 20;

Since both the Employee and Contractor classes implement Payable, they can both include if statements in the implementation of increasePay() to ensure that the provided percentage increase is less than INCREASE_CAP. If in the future the cap changes, you need to change it in only one place — the Payable interface.

Some software developers create Java interfaces that contain only final variables storing important application constants. Implementing such interfaces will make these constants available in the class that implements the interface(s). Not everyone approves such usage of interfaces, as it can create a messy situation when a class that implements interfaces with static constants exposes a new set of public APIs (those final variables) rather than just using these values internally. You can read more about this anti-pattern at the following URL: http://java.sun.com/j2se/1.5.0/docs/guide/language/static-import.html.

Marker Interfaces

Marker interfaces are those that don’t have any methods declared. One example of such an interface is Serializable, covered in Lesson 17. You don’t need to write any implementation of these interfaces; the Java compiler will take care of this for you. Objects of a class that implements a marker interface will support a certain functionality. For example, if a class implements Serializable, JVM will be able to serialize it — turn into a string of bytes (in the server’s JVM) in such a way that the string can be sent to another JVM (on the client’s machine), which will be able to recreate the instance of the object, or de-serialize it.

Also, Java has an operator instanceof (see next section) that can check during the run time if an object is of a certain type. You can use the operator instanceof as shown below to check if the object implements a marker interface, or any other type of interface for that matter:

 if (receivedFromServerObj instancef Serializable) {
     // do something 
 }

Casting

All Java classes form an inheritance tree with the class Object on top of the hierarchy — all Java classes are direct or indirect descendants of Object. When you declare a non-primitive variable, you are allowed to use either the exact data type of this variable or one of its ancestor data types. For example, if the class NJTax extends Tax, each of the following lines is correct.

NJTax myTax1   = new NJTax();
Tax myTax2     = new NJTax();  // upcasting
Object myTax3  = new NJTax();  // upcasting

Java is smart enough to automatically cast an instance of the class to its ancestor. When the variable has more generic type than an instance of the object this is called upcasting. Let’s say the class object has 10 methods and class variables defined, the class Tax (an implicit subclass of Object) adds five more methods and variables (making 15), and NJTax adds another two (total 17). The variable myTax1 will have access to all 17 methods and variables, myTax2 will see only 15, and myTax3 just 10. Why not always use exact types in variable declarations?

Let’s say you need to write a program that will process data about workers of a certain company. Some of them are full-time employees and some are contractors, but you’d like to read their data from some data source and into the same array. Arrays can store only objects of the same type, remember?

Since Java can automatically upcast the objects, you can create a class called Person with two subclasses, Employee and Contractor, and then read the records from a database. Based on the employment type you can then create an appropriate object instance and put it into an array of type Person:

Person workers[] = new Person [100]; 
workers[0] = new Employee("Yakov", "Fain");
workers[1] = new Employee("Mary", "Lou");
workers[2] = new Contractor("Bill", "Shaw");
...

Of course, you could’ve created two separate arrays, one for employees and one for contractors, but I’m laying the foundation here for explaining polymorphism — a powerful concept in object-oriented languages. You’ll see a concrete example of polymorphism in Lesson 7.

At some point you’ll need to process the data from the array workers. In a loop you can test the data type of the current element of the array with the operator instanceof, and then downcast the object (it can’t be done automatically) to Employee or Contractor and process it accordingly.

for (int i; i<20; i++){
     Employee currentEmployee;
     Contractor currentContractor; 
 
     if (workers[i] instanceof Employee){  
          currentEmployee = (Employee) workers[i];
          // do some employee processing here
     } else if  (workers[i] instanceof Contractor){
         currentContractor = (Contractor) workers[i];
         // do some contractor processing here
     }
}

Placing a data type in parentheses in front of another object means that you want to downcast this object to the specified type. You can downcast an object only to one of its descendant data types. Even though the preceding code has correct syntax, it doesn’t represent the best practice for processing similar objects. In the next lesson you’ll see how to use polymorphism to do it in a more generic way.

If a class implements an interface, you can cast its instance to this interface. Say that a class called Employee implements Payable, Insurable, and Pensionable interfaces:

class Employee implements Payable, Insurable, Pensionable {
// implementation of all interfaces goes here 
}

If you are interested in the Insurable behavior of your code, there’s no need to cast the Object to type Employee — just cast it to the Insurable type as shown in the following code fragment. Keep in mind though that if you do so, the variable currentEmployee will expose access only to those methods that were declared in the Insurable interface.

Insurable currentEmployee;
 
if (workers[i] instanceof Insurable){  
  currentEmployee = (Insurable) workers[i];
  // do some insurance-specific processing here
}

Try It

The goal of this assignment is to start using packages, protect data using private variables and define first interfaces. You create a simple program that will increase pay, which will be implemented differently for employees and contractors. After completing this assignment you’ll have working but not perfect code. What can be improved will be explained in Lesson 7.

Lesson Requirements

For this lesson you should have Java installed.

note.ai

You can download the code and resources for this Try It from the book’s web page at www.wrox.com. You can find them in the Lesson6 folder in the download.

Step-by-Step

1. In the Eclipse IDE, create a new project called Lesson6.

2. Create the Payable interface as per Listing 6-1 in the package com.practicaljava.lesson6 you can enter the name of the package in the Eclipse New Java Class window. Declare a final variable there:

int INCREASE_CAP = 20;

3. Create a class called Person:

package com.practicaljava.lesson6;
 
public class Person {
       private String name;
 
       public Person(String name){
              this.name=name;
       }
 
       public String getName(){
              return "Person's name is " + name; 
       }
}
 

4. Create the classes Employee and Contractor in the package com.practicaljava.lesson6. Each class should extend Person and implement Payable. Use the Eclipse button Add to automatically include the Payable interface in declarations of the classes Employee and Contractor.

5. Check your file system to ensure that the files were created in your workspace in the directory com/practicaljava/lesson6.

6. Create a class called TestPayIncrease with a method called main(). Don’t specify any package — this class will be created in a different directory.

7. Try to create an instance of the class Employee in the method main() of TestPayIncrease:

Employee myEmployee = new Employee();

You’ll get an error: Employee can’t be resolved to a type. No wonder — it’s located in a different package. Move the mouse over Employee and Eclipse will offer you a fix — add an import statement:

import com.practicaljava.lesson6.Employee;

Select this fix and later add the import statement for all required classes.

8. In the main() method of the class TestPayIncrease create an array of employees and contractors and call the function increasePay() for each element of the array:

public static void main(String[] args) {
 
        Person workers[] = new Person[3];
             workers[0] = new Employee("John");
             workers[1] = new Contractor("Mary");
             workers[2] = new Employee("Steve");
 
                   Employee currentEmployee;
                   Contractor currentContractor; 
 
              for (Person p: workers){
                  if (p instanceof Employee){  
                       currentEmployee = (Employee) p;          
                       currentEmployee.increasePay(30);
          
                  }else if  (p instanceof Contractor){
                      currentContractor = (Contractor) p;
                      currentContractor.increasePay(30);
                  }
             }
}

9. Implement the increasePay() method in Employee — don’t put any restrictions on pay increases. Here’s the body of increasePay():

      System.out.println("Increasing salary by " + percent + "%. "+ getName()); 
      return true; 

10. Implement the increasePay() method in the class Contractor. If the percentage of the increase is below INCREASE_CAP, print a message similar to the one in the preceding code. Otherwise, print a message explaining that you can’t increase a contractor’s rate by more than 20 percent.

11. Run the TestPayIncrease program. It should produce output similar to the following:

Increasing salary by 30%. Person's name is John
Sorry, can't increase hourly rate by more than 20%. Person's name is Mary
Increasing salary by 30%. Person's name is Steve 

To get the sample database files you can download Lesson 6 from the book’s website at www.wrox.com.

cd.ai

Please select Lesson 6 on the DVD with the print book, or watch online at www.wrox.com/go/fainjava to view the video that accompanies this lesson.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.142.62