In the last lesson we discussed Abstract Classes, which allow us to leave portions of a class unimplemented. This allows us to do a few really important things:
This entire process is known as Abstraction: The process of hiding unnecessary details of an object to the user, and only show what is relevant. Abstraction is an extremely useful tool that allows two programmers (or teams of programmers) to develop code that is supposed to work together simultaneously without having to look at each others code.
As an example, imagine you are working with a partner to create a periodic payment system (a system where after a specified period of time, a payment is made, like a mortgage or phone bill payment). Your system requires 2 classes: One that specifies a bank account, another that specifies how to make a periodic payment. If each of you decides to implement one class separately, then there is a bit of a problem! How can you make a periodic payment, if you don't know anything about an account? This is where abstraction shines! Enter, the Java Interface:
Now, with this interface, the partner creating the periodic payment class can work!
A Java interface creates a "contract" that can be programmed against, without an actual implementation. The JavaDoc provided tells us what each method should do, precisely.
In this interface, we can see that both methods return boolean values that indicate the success or failure of the operation. In addition, the amount deposited or withdrawn must be non-negative, by specification. Each of those conditions can be programmed against without needing to know exactly how it works in the background.
Looking at the above example, we can see how Java does interfaces. Instead of "class", we use the keyword "interface." Each method is, by default, public, so we can skip that keyword on the method definitions. Notice that, like abstract classes, our methods have no bodies and are terminated with a semicolon (;). In fact, in general, Java interfaces follow the general template of:
public interface NAME {
RETURN_TYPE METHOD_NAME(PARAMETERS);
...
}
Where the number of method signatures depends on the interface being defined.
There are two main conventions we should try to follow when creating interfaces:
To agree to the "contract", your class must implement the defined interface:
public class BankAccount implements IBankAccount {
// Instance Variable should go here.
public BankAccount() {
// You will likely need some constructors.
}
public boolean withdraw(double amount) {
// Implementation goes here!
// Don't forget to return a boolean value!
}
public boolean deposit(double amount) {
// Implementation goes here!
// Don't forget to return a boolean value!
}
// Is there any other methods you need to create?
// Make sure to implement them too!
}
In the above file (which won't compile as it is. Go ahead! Try!) the BankAccount Class implements the IBankAccount interface. This means that BankAccount.java needs to have at least 2 methods:
You may have, and will probably need, other methods, but those two are absolutely necessary. It is important to also note that the name of your class does NOT have to be the same as the interface without the I. For example:
public class SavingsAccount implements IBankAccount {
public SavingsAccount() {
// You will likely need some constructors.
}
public boolean withdraw(double amount) {
// Still need to implement this function!
}
public boolean deposit(double amount) {
// Still need to implement this function!
}
}
1. With a partner implement 2 classes: PeriodicPayment.java and SimpleBankAccount.java, using the IBankAccount.java inteface provided above. Once you start, try not to talk to each other about how to implement the classes. The PeriodicPayment.java file should have, at minimum, one method:
2. Each partner should now create two new implement two new classes:
3. Write a Test Harness and set of black box tests for each of your accounts that makes use of the PeriodicPayment.java file you created to perform the actual deposits and withdraws.