Welcome to Lesson 14!
Learning Objectives By the end of today's class, you should know...
- What is an interface?
- What is the purpose of an interface?
- What is the difference between an abstract class and an interface?
- What is the Comparable Interface and what is the proper way to override compareTo for a class?
1. InterfacesIntroduction to Interfaces
- An interface is a group of related method signatures which provide a guarantee of the behavior a class will provide.
- For example:
public interface MyInterface { //constant variables
//method signatures
public void myMethod1(); public void myMethod2();
}
- A Java interface declares a set of methods that an object must define
- An interfaces can be considered a contract that a class is guaranteeing to uphold
- This contract is that any class that implements the interface must define all the methods in interface
- None of the methods in the interface have a body*
- (*unless they are defined to be default or static (a new feature since Java 8))
- All interface methods are implicitly public and abstract
- (*unless they are defined to be default or static (a new feature since Java 8))
- Thus, you can omit the modifiers public and abstract from your method signatures
- Any class that implements an interface must define the bodies of all the abstract methods contained in the interface.
- In addition, an interface may define constant variables.
- If you declare a variable inside of an interface, that variable automatically become public, static and final -- even if you do not explicitly declare it to be so.
- Thus, you can omit the modifiers public, static and final from your variable declaration.
- To inherit from an interface, you must use the keyword implements
public class MyClass implements MyInterface {
- When a class implements an interface there is a formal guarantee about the behavior it will provide
- If a class implements an interface,
all methods defined by that interface must appear in its source code
before the class will compile.
- The next example shows an interface for a programmer-defined system with a method that any object must implement for drawing
Example Interface
Example Interface Implementation
Writing Custom Interfaces
- Declaring an interface is similar to declaring a class
- You use the keyword
interface instead of class - General syntax:
public interface InterfaceName {
[public] [static] [final] dataType CONSTANT_NAME = value;
returnType methodName(parameterList);
}
- Where:
- InterfaceName: the name you make up for the interface
- dataType: the type of constant
- CONSTANT_NAME: the name you make up for the constant
- value: the value of the constant
- returnType: the type of value returned by the method, or
void for no return value - methodName: the name you make up for the method
- parameterList: the types and names of the parameters
- The constant variables and abstract methods are optional
- For example:
public interface Drawable {
public static final int FILLED = 0;
public static final int UNFILLED = 1;
public void draw();
}
- Declares an abstract method named
draw()
- You cannot write the implementation of the method in the interface
- Instead, you write the implementation of the method in any class that
implements the interface
- All methods are
public and abstract by default - Similarly, all constants are
public static final by default:
- Using the keywords
public static final is optional - Though optional, they help to document the scope
- Any interface method that is implemented becomes an instance method
Extending Interfaces
- You can derive new interfaces from an existing one just like classes can extend classes
- One difference is that an interface can extend multiple interfaces
- Syntax:
public interface InterfaceName
[extends SuperInterface1 [, SuperInterface2]...] { }
- Where:
- InterfaceName: the name you make up for the interface
- SuperInterfaceX: the name of the interface being extended
- For example, we could split our
Drawable interface into two superinterfaces and one subinterface - The superinterfaces now contain the constants:
interface DrawingConstants {
public static final int FILLED = 0;
}
interface DrawingConstants2 {
public static final int UNFILLED = 1;
}
- The subinterface contains the method declaration:
interface Drawable
extends DrawingConstants, DrawingConstants2 {
public void draw();
}
- The new
Drawable has the same capabilities as the old Drawable
Implementing Interfaces
- Any class can choose to implement zero or more interfaces
- Syntax:
public class ClassName [extends SuperClass]
[implements Interface1 [, Interface2]...] { }
- Where:
- ClassName: the name you make up for the class
- SuperClass: the name of the parent class
- InterfaceX: the name of the interface being implemented
- Classes implementing an interface add the keyword
implements in the class header - After the
implements keyword, code a comma-delimited list of interface names - For example:
public class Map implements Drawable
- Class
Map must now implement the method draw() - Note that you can refer to a constant declared in the interface without the interface name if you implement the interface
int filled = UNFILLED; - A class that implements an interface must implement all methods of the interface:
public class Map implements Drawable {
int filled = UNFILLED;
public void draw() {
System.out.println("Drawing a Map.");
}
}
Abstract Classes vs. Interfaces
- Abstract classes and interfaces have many similarities and differences
- Both abstract classes and interfaces allow you to define constants
- In addition, both abstract classes and interfaces allow you to declare abstract methods
- These similarities mean that interfaces can sometimes be used instead of an abstract class
Advantages of an Abstract Class
- An abstract class can have instance variables while an interface cannot
- An abstract class can define regular methods while interfaces cannot
Advantages of an Interface - Multiple Inheritance
- In Java, a class can only inherit from (extend)
one class to avoid what is known as the "diamond problem" or sometimes
"the deadly diamond of death" (Yikes!)
- The diamond problem can occur when two classes define methods with identical signatures
- If
a subclass inherits from both these classes and tries to invoke one of
the two methods, it will not be clear which method should be invoked
(the one defined in the first class or the second class)
- However, a class can directly implement many interfaces
- Because
(most) methods in interfaces are abstract, and thus have no bodies, it
will be the job of any class extending the interface to define the body
of the method
- Additionally, interfaces can inherit from other interfaces
- Recall
that to inherit from more than one interface, a class can give a
comma-separated list of interfaces in its signature, and use the keyword
implements
public class MyClass implements MyInterface1, MyInterface2, MyInterface3 { - Similarly, an interface can provide a comma-separated list of other interfaces from which it inherits
public interface MyInterface extends MyInterface1, MyInterface2, MyInterface3 {
When Will You Need Interfaces?
- You often need to implement interfaces in the Java API
- In particular, interfaces are used in coding graphical user interfaces
- You design interfaces most often when creating larger applications
Default and Static Methods- Since Java 8, interfaces can also define methods with bodies.
- These methods must be declared using the keyword default or static
- The
reason behind the inclusion of these methods was to allow interfaces to
more easily adapt and evolve over time, and allow for backwards
compatibility.
- Assuming you have defined an interface called Trained:
public interface Trained {
boolean canSit(); boolean canStay();
boolean canShake();
} - Any class that implements Trained must define the body of all of its abstract methods or these classes will not compile.
- Therefore, if you later decide to add another method...
boolean canLieDown();
- ... any class that implements your interface will now break - and you will have to deal with a number of angry programmers
- To allow interfaces more to easily to evolve over time, Java 8 allowed default and static methods.
- Now, a new method can be added to an interface as long as it is declared either default or static and its body defined.
- All classes implementing the interface will automatically inherit the method with its full definition
default boolean canLieDown() { System.out.println("Lying down");
return true;
} - With the introduction of these default and static methods, Java risked opening itself up to the Diamond Problem.
- To
resolve this problem, if a class is implementing two interfaces
containing a method with the same signature and at least one of these
methods is default or static method, the Java compiler requires that the class implementing the interfaces overload the method causing the problem.
- The
overloaded method definition takes precedence over any default or
static implementation, thus removing the source of confusion.
- In Effective Java, Joshua Bloch states that default methods are "risky" and advises against adding any new methods to interfaces once the interface has been released.
- He states that when you write an interface, it should be "designed for posterity"
- I won't cover default methods, as we will not be using
them in our assignments. However, you can read more information about
them here and here.
More Information
Activity 14.1: Accounts Payable Interface (10 pts)- Open the Address.java and Employee.java classes
- Next, write over the contents of your Employee.java file with the following code:
/** * Employee.java * @author * CIS 36B, Activity 14.1 */
import java.util.Scanner;
public abstract class Employee extends Person { private static int numEmployees = 0; private String department; private double salary; /** * Default constructor for the * Employee class. Calls the * default constructor of the * superclass and also initializes * salary and department to default * values */ public Employee() { super(); salary = 0.0; department = "department unknown"; } /** * Multi-argument constructor for the * Employee class. Calls the * multi-argument constructor of the * superclass and also initializes * salary and department to the * specified values */ public Employee(String name, int age, String gender, Address a, double salary, String department) { super(name, age, gender, a); this.salary = salary; this.department = department; } /** * Returns the annual salary * @return the salary */ public double getSalary() { return salary; } /** * Returns the department * of the employee in the company * @return the department */ public String getDepartment() { return department; } /** * Returns the total number of employees * @return the number of employees */ public static int getNumEmployees() { return numEmployees; } /** * Updates the salary with a new value * @param salary the new salary */ public void setSalary(double salary) { this.salary = salary; } /** * Updates the department of the employee * @param department the new department */ public void setDepartment(String department) { this.department = department; } /** * Increments numEmployees */ public static void updateNumEmployees() { numEmployees++; } /** * Defines how this category of employee * works */ public abstract void doesWork();
/** * To String method for an employee * Calls the toString method of the * superclass and also appends the department * and salary information */ @Override public String toString() { return super.toString() + "\nSalary: $" + salary + "\nDepartment: " + department; } /** * Determines whether this is equal * to another Object by comparing their name, * types and subsequently, their name, age, gender * and address * @return whether two Objects are equal */ @Override public boolean equals(Object o) { if (o == this) { return true; } else if (! (o instanceof Employee)) { return false; } else { Employee e = (Employee) o; return (super.equals(o) && salary == e.salary && department.equals(e.department)); } } }
- Notice that this class has now been made abstract, and contains an abstract method doesWork()
- Next, create two subclasses of Employee - Manager.java and Programmer.java.
- Copy and paste the below code into the Manager.java file and write the given methods:
/** * Manager.java * @author * CIS 36B, Activity 14.1 */ public class Manager extends Employee { private String title; private static double taxBracket = .32; /** Constructors **/ /** * Assigns default values to all fields * by calling the 7-argument constructor * name: name unknown * age: 0 * gender: gender unknown
* address: null * salary: 0.0 * department: department unknown * title: title unknown */ public Manager() { } /** * Calls 6-argument constructor of the super class * and assigns title * @param name the manager name * @param age the manager age * @param gender the manager gender * @param a the manager address * @param salary the manager salary * @param department the manager department * @param title the manager title */ public Manager(String name, int age, String gender, Address a, double salary, String department, String title) { } /**Accessors**/ /** * Returns the manager's title * @return the title */ public String getTitle() {
return "";
} /** * toString method for the Manager class * calls the super class toString and also * adds Title: <title> to the String */ @Override public String toString() {
return "";
} /**Mutators**/ /** * Sets the current title of the manager * @param title the new title */ public void setTitle(String title) { return; } /**Additional Operations**/ /** * "does work" by printing out the message * Work: Telling people what to do */ public void doesWork() {
return;
} }
- Copy and paste the below code into Programmer.java and implement the given methods:
/** * Programmer.java * @author * CIS 36B, Activity 14.1 */ public class Programmer extends Employee { private String assignedProject; private static double taxBracket = .24; /**Constructors**/ /** * Assigns default values to all fields * by calling the 7-argument constructor * name: name unknown * age: 0 * gender: gender unknown * address: null * salary: 0.0 * department: department unknown * assignedProject: project unknown */ public Programmer() { } /** * Calls constructor of the super class * and assigns project * @param name the manager name * @param age the manager age * @param gender the manager gender * @param a the manager address * @param salary the manager salary * @param department the manager department * @param project the assigned project */ public Programmer(String name, int age, String gender, Address a, double salary, String department, String project) { } /**Accessors**/ /** * Returns the title of the programmer's * currently assigned project * @return the assigned project title */ public String getAssignedProject() { return ""; } /** * toString method for the Manager class * calls the superclass toString and also * adds Project: <assignedProject> to the String */ @Override public String toString() { return ""; } /**Mutators**/ /** * Sets the assigned project * @param project the new project */ public void setAssignedProject(String project) { return; } /**Additional Operations**/ /** * "does work" by printing out the message * Work: Writing code */ public void doesWork() { return; } }
- Next, add an Interface (File->New->Interface) to your project called AccountsPayable.java
- Copy and paste the below code into your file:
/** * AccountsPayable.java * @author * CIS 36B, Activity 14.1 */ public interface AccountsPayable { double salaryAfterTaxes(); void printPayCheck(); } - Now, update Manager.java and Programmer.java so that they implement the AccountsPayable interface.
- You
should not get an error message for these two classes as they must
define the methods defined in AccountsPayable.java in order to compile.
- Add the following comment and method signature to Manager.java under the Accessors section of the class:
/** * Calculates the net salary after taxes * net salary = gross salary * (1 - taxBracket) * @return the net salary */ @Override public double salaryAfterTaxes() { return 0.0; } - Then, write the method body as described in the comment.
- Next, add the following comment and method signature to Manager.java in the Additional Operations section of the class:
/** * Prints out address to mail the paycheck * Mail to: * Title + Name * Address */ @Override public void printPayCheck() { System.out.println("Mail to:"); } - Now implement the rest of the method body.
- In Programmer.java, add the following comment and method to the Accessor section of the class, and then fill in the method body:
/** * Calculates the net salary after taxes * net salary = gross salary * (1 - taxBracket) * @return the net salary */ @Override public double salaryAfterTaxes() { return 0.0; } - Finally,
add the following comment and method to the Additional Operations
section of the Programmer class, and then fill in the method body:
/** * Prints out the correct address to mail the * paycheck, in the format: * Mail to: * Mr./Ms./Mx. (note use if statements here) * Name * Address */ @Override public void printPayCheck() { System.out.println("Mail to:"); } - Lastly, write over your EmployeeTest.java file with the following code:
/** * EmployeeTest.java * @author * CIS 36B, Activity 14.1 */
import java.util.Scanner;
public class EmployeeTest { public static void main(String[] args) { Scanner input = new Scanner(System.in); String name, gender, street, department, title, project; int age, number; double salary; Manager employee1 = null; Programmer employee2 = null; System.out.print("Welcome, Manager!\n\nEnter your name: "); name = input.nextLine(); System.out.print("Enter your age: "); age = input.nextInt(); System.out.print("Enter your gender (M/F/O): "); gender = input.next(); System.out.print("Enter the street number of your address: "); number = input.nextInt(); System.out.print("Enter the name of the street: "); input.nextLine(); street = input.nextLine(); Address address = new Address(number, street); System.out.print("Enter your department in the company: "); department = input.nextLine(); System.out.print("Enter your title: "); title = input.nextLine(); System.out.print("Enter your annual salary ($150,000-200,000): $"); salary = input.nextDouble(); employee1 = new Manager(name, age, gender, address, salary, department, title); System.out.println("\nWhat you did at work today:"); employee1.doesWork(); System.out.format("\nGreat job!\n\n" + "After taxes this year, you earned: $%.2f\n\n", employee1.salaryAfterTaxes()); System.out.println("Cutting your paycheck...\n"); employee1.printPayCheck(); System.out.print("\nWelcome Programmer!\nEnter Your Name: "); input.nextLine(); name = input.nextLine(); System.out.print("Enter your age: "); age = input.nextInt(); System.out.print("Enter your gender (M/F/O): "); gender = input.next(); System.out.print("Enter the street number of your address: "); number = input.nextInt(); System.out.print("Enter the name of the street: "); input.nextLine(); street = input.nextLine(); Address address2 = new Address(number, street); System.out.print("Enter your department in the company: "); department = input.nextLine(); System.out.print("Enter the name of project you are working on: "); project = input.nextLine(); System.out.print("Enter your annual salary ($110,000-150,000): $"); salary = input.nextDouble(); employee2 = new Programmer(name, age, gender, address2, salary, department, project); System.out.println("\nWhat you did at work today:"); employee2.doesWork(); System.out.format("\nGreat job!\n\n" + "After taxes this year, you earned: $%.2f\n\n", employee2.salaryAfterTaxes()); System.out.println("Cutting your paycheck...\n"); employee2.printPayCheck(); input.close();
} } - When
your program works identically to what is shown in the sample output
below, submit Employee.java, Manager.java, and Programmer.java to Canvas.
Sample Output:
Welcome, Manager!
Enter your name: Jacqueline Nguyen Enter your age: 47 Enter your gender (M/F/O): F Enter the street number of your address: 555 Enter the name of the street: Richie Rich St Enter your department in the company: Research and Development Enter your title: Engineering Manager Enter your annual salary ($150,000-200,000): $189000
What you did at work today: Work: Telling people what to do.
Great job!
After taxes this year, you earned: $128520.00
Cutting your paycheck...
Mail to: Engineering Manager Jacqueline Nguyen 555 Richie Rich St
Welcome Programmer! Enter Your Name: Ichiro Yamamoto Enter your age: 42 Enter your gender (M/F/O): M Enter the street number of your address: 4321 Enter the name of the street: Pacific Palisades Dr Enter your department in the company: Research and Development Enter the name of project you are working on: Rocket Launch Interface Enter your annual salary ($110,000-150,000): $135000
What you did at work today: Work: Writing code.
Great job!
After taxes this year, you earned: $102600.00
Cutting your paycheck...
Mail to: Mr. Ichiro Yamamoto 4321 Pacific Palisades Dr
2. Implementing the Comparable Interface and the CompareTo Method
The Comparable Interface- Unlike equals, the
compareTo method is not defined as part of the Object class, but rather is the sole abstract method in the Comparable Interface:
public interface Comparable<T> { int compareTo(T t); } - To define the
compareTo method for your class, you must implement the Comparable Interface. - For example,
public class Dog implements Comparable<Dog> { - Notice
that the Comparable interface is generic - a topic we will discuss in
detail next class - and we are, thus, required to put the type of Object
to compare inside of the angled brackets: <Dog>
Defining compareTo() for a ClassThe compareTo() Contract:- Once the signature for a class has been updated to implement the Comparable interface, we are now required to define
compareTo or the class will not compile - When writing the
compareTo method it is important to be mindful of the "compareTo Contract," as follows:
- The implementor must ensure
sgn(x.compareTo(y)) == -sgn(y.compareTo(x))
for all x and y . (This
implies that x.compareTo(y) must throw an exception iff
y.compareTo(x) throws an exception.)
- The implementor must also ensure that the relation is transitive:
(x.compareTo(y) > 0 && y.compareTo(z) > 0) implies
x.compareTo(z) > 0 .
- Finally, the implementor must ensure that
x.compareTo(y)==0
implies that sgn(x.compareTo(z)) == sgn(y.compareTo(z)) , for
all z .
where, the notation
sgn( expression) above designates the mathematical
signum function, which is defined to return one of -1 ,
0 , or 1 according to whether the value of
expression is negative, zero, or positive, respectively. - Note that Oracle strongly recommends that the
equals and compareTo methods correspond, such that if x.equals(y) then x.compareTo(y)==0. However,
it is not a requirement of the method (and should be noted in the
comments for the method that there is an irregular comparison)
- When writing
compareTo , the type of Object passed in as a parameter must match that of the type in <> in the class signature.
public class Dog implements Comparable<Dog> { private String name; private int age;
@Override public int compareTo(Dog d) { //method body
}
}
- The best approach to writing this method is to start by comparing the most significant field (i.e. name for the Dog class)
- If
these two fields are equal, then compare the next most significant
field (i.e. age), and so on until you find an unequal field
- I would recommend calling the equals method as a first step to determine whether the two objects are equal.
- Below is an example of a
compareTo method for the Dog class:
public class Dog implements Comparable<Dog> { private String name; private int age;
@Override public int compareTo(Dog d) { if (this.equals(d)) { return 0;
} else if (!(name.equals(d.name))) { //names not equal, compare names
return name.compareTo(d.name);
} else { //names are equal, compare ages
return Integer.compare(age, d.age);
}
}
}- In the above implementation, I made use of the
compareTo method already defined for String, and return this value in the case that the names were unequal. - Additionally, as recommended by Joshua Bloch in Effective Java, I made use of the static
compare method of the Integer
class to determine an appropriate value to return when comparing the
two ints. These static comparison methods are considered preferable (and
less verbose) to the use of < and > operators that might
otherwise be used to compare primitive types.
public class Dog implements Comparable<Dog> { private String name; private int age; private boolean isChildFriendly;
@Override public int compareTo(Dog d) { if (this.equals(d)) { return 0;
} else if(!(name.equals(d.name))) { //names not equal, compare names
return name.compareTo(d.name); } else if (age != d.age) { return Integer.compare(age, d.age);
return Boolean.compare(isChildFriendly, d.isChildFriendly);
}
}- When
comparing primitive types inside of a compareTo method, use one of the
static comparison methods defined within these Object classes:
Activity 14.2: Sorting Employees (10 pts)
- Open up Address.java, Employee.java and EmployeeTest.java from the last activity
- Next, alter the signature for the Address class to implement the Comparable interface.
- Add the following method signature to Address, and implement compareTo as described in the comment:
/**
* Compares two addresses and returns * an integer value: 0 if equal addresses * negative if this address comes before a * positive if this address comes after a * To determine ordering, sorts first by street * name and second by street number */ @Override public int compareTo(Address a) { return -1;
} - You can now close Address.java.
- Inside of Employee.java, alter the class signature so that it implements the Comparable interface.
- Add the following method signature to the Employee.java class and implement compareTo as described in the comment:
/**
* Compares two Employees and returns * an integer value: 0 if equal Employees * negative if this Employee comes before e * positive if this Employee comes after e * To determine ordering, sorts first by name, * next by age, next by gender, next by address, * next by department and finally by salary */ @Override public int compareTo(Employee e) {
return -1;
} - You can now close Employee.java
- Finally, replace EmployeeTest.java with the following code:
/** * EmployeeTest.java * @author * CIS 36B, Activity 14.2 */
import java.util.Scanner; import java.util.ArrayList;
public class EmployeeTest { private ArrayList<Employee> employees = new ArrayList<Employee>(); public static void main(String[] args) { EmployeeTest t = new EmployeeTest(); t.employees.add(new Manager("Jacqueline Nguyen", 47, "F", new Address(555, "Richie Rich St"), 189000, "Research and Development", "Engineering Manager")); t.employees.add(new Programmer("Ichiro Yamamoto", 42, "M", new Address(4321, "Pacific Palisades Dr"), 135000, "Research and Development", "Rocket Launch Interface")); t.employees.add(new Manager("Sanjiv Sardar", 65, "M", new Address(445, "Howard St"), 175000, "Marketing", "Communications Manager")); System.out.println("Before Sorting:\n"); t.printEmployees(); t.bubbleSort(); System.out.println("\n\nAfter Sorting:\n"); t.printEmployees(); } public void printEmployees() { for (int i = 0; i < employees.size(); i++) { System.out.println(employees.get(i) + "\n"); } } public void bubbleSort() { for (int i = 0; i < employees.size() - 1; i++) { for (int j = 0; j < employees.size() - i - 1; j++) { if (employees.get(j).compareTo(employees.get(j + 1)) > 0) {
//Fill in here!
} } } } }
- Complete the implementation of BubbleSort
- When you are finished, run the program and verify that you get the output as shown in the example output below.
- Then, upload Address.java, Employee.java, and EmployeeTest.java to Canvas
Before Sorting:
Name: Jacqueline Nguyen Age: 47 Gender: F Address: 555 Richie Rich St Salary: $189000.0 Department: Research and Development Title: Engineering Manager
Name: Ichiro Yamamoto Age: 42 Gender: M Address: 4321 Pacific Palisades Dr Salary: $135000.0 Department: Research and Development Project Rocket Launch Interface
Name: Sanjiv Sardar Age: 65 Gender: M Address: 445 Howard St Salary: $175000.0 Department: Marketing Title: Communications Manager
After Sorting:
Name: Ichiro Yamamoto Age: 42 Gender: M Address: 4321 Pacific Palisades Dr Salary: $135000.0 Department: Research and Development Project Rocket Launch Interface
Name: Jacqueline Nguyen Age: 47 Gender: F Address: 555 Richie Rich St Salary: $189000.0 Department: Research and Development Title: Engineering Manager
Name: Sanjiv Sardar Age: 65 Gender: M Address: 445 Howard St Salary: $175000.0 Department: Marketing Title: Communications Manager
Wrap Up:
- Answer the Lesson 14 Practice Exam Questions on Canvas
Upcoming Assignments:
- Activity 14.1 and 14.2 due Thursday at 11:59pm
- Lesson 14 Practice Exam Questions due Thursday at 11:59pm
- Peer Reviews of Lesson 13 and 14 Practice Exam Questions due Saturday at 11:59pm
- Quiz 5 due Friday at 11:59pm
- Lab 7 due Monday at 11:59pm
~ Have a Great Weekend! ~ |