Interfaces
Objective #1: Describe an interface.
- An interface is a list of method signatures that establish behavior that unrelated classes can share.
Here is an example of an interface named Movable. I made up this interface as a simple example; it is not a famous interface that is built-into Java.
public interface Movable
{
public void move(int amount);
}
- There is only one method signature in this interface for a method named move.The line of code void move(int amount); is a method signature. Notice the semicolon at the end of the statement. Also notice that there is no body to this move method. In other words, the move method has no implementation. An interface cannot contain implementation for the methods that it includes.
- A method is called an abstract method when it has no implementation.
- An interface is typed as a separate file just like a class however the keyword interface is used instead of class. The name of an interface is always capitalized for good style just like the names of classes. Often the name of an interface ends with the suffix -able as in Movable, Comparable, Clickable, etc. This is not a required by good style practices though. Also the name of an interface is often an adjective, unlike the names of classes which are usually nouns.
- All methods in an interface are automatically be public so typing the actual keyword public is optional. In the example above, the line of code
public void move(int amount);
could be replaced with
void move(int amount);
- An interface cannot contain any instance variables (i.e. properties). Because it has not instance variables, it doesn't make sense for an interface to contain accessor or modifier methods.
- An interface cannot contain any constructors.
- Often constants are placed in interfaces as well as in this version of my Movable interface
public interface Movable
{
public static final int SPEED_OF_LIGHT = 3e8;
public void move(int amount);
}
Remember that it makes sense for all objects that make use of a constant to share the same constant so we use the static modifier keyword.
A constant is a constant and cannot be changed by client programmers because of the use of the final modifier keyword.
Constants, unlike instance variables (i.e. properties), should be public and not private.
The letter e is used to express the number in exponential notation. 3e8 could have been typed as 300000000 where there are 8 zeroes.
Objective #2: Understand how to use interfaces.
- Consider what types of things in the world move. Humans can certainly move. So can bugs. Inanimate objects such as cars and airplanes can also move. So if you were to develop classes for these kinds of objects, you would probably include methods like walk, run, crawl, drive, and fly! But it's better to be more abstract than that in order for future programmers to work with your classes more easily and be able to predict the names of methods that they would likely use in a client program.
So by agreeing with other class developers in the world to agree on and use the general name move rather than various synonyms or more specific names for methods like walk, run, crawl, etc., your class becomes more user-friendly to future client programmers! Therefore, when you develop a Human class for example, you would write it so that it implements a widely-accepted interface such as Movable. The first line of your Human class must be typed out as
public class Human implements Movable
{
// constructors, etc.
}
In doing so though, you must then type out and fully implement a method named move that returns nothing (i.e. void) and that accepts one int parameter exactly since the method signature for the move method in the Movable interface accepts an int parameter and does not return anything.
So your Human class would look something like this
public class Human implements Movable
{
private int myXLocation;
// constructors, accessors, modifiers
public void move(int amount)
{
myXLocation += amount;
}
// other interesting methods & properties
}
While an Airplane class might look like this
public class Airplane implements Movable
{
private int myAltitude;
// constructors, accessors, modifiers
public void move(int amount)
{
myAltitude += amount;
}
// other interesting methods & properties
}
- When a class implements an interface, it can also be said that the class realizes the interface. To realize an interface, a class must fully implement each of the methods listed in the interface.
- Many classes can realize the same interface. In fact, that's the whole idea of using interfaces. An unlimited number of unrelated classes (e.g. Human, Bug, Car, Airplane) can implement the same interface. Each of these unrelated classes may implement the methods in the shared interface in different ways. Since a Human object moves differently from a Bug which moves differently from an Airplane, we say that the move method is polymorphic. Like other object-oriented programming languages, Java takes advantage of this concept called polymorphism to make it easier for client programmers and class developers to write code and create applications more efficiently without having to look up specific names of methods in API's.
- More than one interface can be realized by a class as in
public class Bug implements Movable, Comparable
where both interfaces Movable and Comparable are realized by the Bug class. A comma is used to separate the interfaces.
- Here is a complete example of an interface, a class that realizes that interface and a client program that makes use of an object that comes from the class that realizes the interface:
public interface Movable
{
public static final int SPEED_OF_LIGHT = 3e8;
public void move(int amount);
}
//////////////////////////////////////////////////////////
public class Bug implements Movable
{
private int myX;
public void move(int amount)
{
myX += amount;
}
public void eat()
{
System.out.println("That tasted good.");
}
}
//////////////////////////////////////////////////////////
public class InterfaceDemo
{
public static void main(String[] args)
{
Bug buggy = new Bug();
buggy.move(5);
System.out.println("Bugs cannot move faster than " + Bug.SPEED_OF_LIGHT);
buggy.eat();
Movable bugsy = new Bug();
bugsy.move(5);
bugsy.eat(); // illegal since eat isn't in Movable interface
}
}
- An interface itself can realize other interfaces. For example, you could have
public interface Locatable implements Viewable, Positionable
Objective #3: Understand the role of the Comparable interface & be able to implement the compareTo method. AS OF 2016-17, THE Comparable INTERFACE IS NOT ON THE AP EXAM HOWEVER YOU MUST KNOW HOW TO USE THE compareTo METHOD ESPECIALLY WITH STRINGS.
- There is a famous, built-in interface named Comparable that is used by Java programmers to compare objects to one another. You must know how to use this interface on the AP Exam.
- The Comparable interface
includes only one method signature and looks like this however you would never have to write it out on the AP exam nor would you have to type it out in a Java project since it's built into Java. You may however have to write it out on quizzes or tests in this Java class.
public interface Comparable
{
public int compareTo(Object other);
}
- The compareTo method from the Comparable interface must return an int. It must accept one parameter that has the data type Object. Object is used as a wildcard data type so that any class can make use of the Comparable interface. In the examples below, you'll see how casting is used to cast the parameter from the Object data type to the data type of the specific class in which you are implementing the compareTo method. It is conventional to return a positive number such as 1 if the this object is greater than the parameter object. It is conventional to return a negative number such as -1 if the parameter object is greater than the this object. It is conventional to return zero, if the two objects are equal.
- The tricky part to using the Comparable interface though is that you have to implement its compareTo method in a class that you are developing. For example, if you are creating a Student class, you must decide how two Student objects should be compared.
- For example, if you had a Student class with myGPA, myLastName, and myHeight properties, you could choose either of these properties or a combination of the properties to be the criteria that determines whether one student is considered to be greater than another student. You could override the compareTo method in this manner where students are compared by their grade point averages
public int compareTo(Object other)
{
if (this.myGPA > ((Student) other).myGPA)
{
return 1;
}
else if (this.myGPA < ((Student) other).myGPA)
{
return -1;
}
return 0;
}
or you could override it like this where last names are used to alphabetically compare students
public int compareTo(Object other)
{
return this.myLastName.compareTo(((Student) other).myLastName);
}
or you could even override it like this where a student's GPA is mathematically added to a student's height even though this is a ridiculous way to compare students
public int compareTo(Object other)
{
if (myGPA + myHeight > ((Student) other).myGPA + ((Student) other).myHeight)
{
return 1;
}
else if (myGPA + myHeight < ((Student) other.)myGPA + ((Student) other).myHeight)
{
return -1;
}
return 0;
}
Observe that the use of the this operator is optional. But it is necessary to cast the explicit parameter named other in the examples above to a Student object since other is passed as an Object.
- When overriding the compareTo method, the exact method header
public int compareTo(Object other)
must be used since this is the method header found in the Comparable interface. Any parameter name can be substituted for other though. When overriding a method from an interface, the method must be public. It cannot be private.
- Remember that the compareTo method must return an int. It must accept an Object parameter! In general, the compareTo method returns an integer value less than zero if the this object is less than the parameter to the method. It returns an integer value greater than zero if the this object is greater than the parameter. It returns zero if the two are equal.
- The Comparable class does not require the equals method to be implemented since the equals method is inherited by all classes from the Object class. But it is a good idea to override the equals method, especially if you are implementing the Comparable class. Here is an example of how you could override the equals method inherited from the Object class:
public boolean equals(Object other)
{
return other != null && compareTo(other) == 0;
}
Objective #4: Use the compareTo method in client programs. Note that you will probably only have to use the compareTo method from the String class on the AP exam.
- If a given class such as Student implements the Comparable interface then a client programmer can use the compareTo method to compare two Student objects. Note that the < and > symbols do not work for objects that come from classes. Those relational operators only work for primitive data types int and double.
- Here is an example of a client program in which two Student object variables austin and brock are being compared.
public class ComparableDemo
{
public static void main(String[] args)
{
Student student1 = new Student();
Student student2 = new Student();
student1.setGPA(2.5);
student2.setGPA(2.4);
student1.setName("austin");
student2.setName("Brock");
// the example below makes use of the compareTo method that is implemented in the Student class
if (student1.compareTo(student2) > 0)
{
System.out.println(student1.getName() + " has a higher grade than " + student2.getName());
}
else if (student1.compareTo(student2) < 0)
{
System.out.println(student1.getName() + " has a lower grade than " + student2.getName());
}
else
{
System.out.println(student1.getName() + " has the same grade as " + student2.getName());
}
// the example below makes use of the compareTo method that is implemented in the String class
if (student1.getName().compareTo(student2.getName()) > 0)
{
System.out.println(student1.getName() + " is alphabetically less than " + student2.getName());
}
else if (student1.compareTo(student2) < 0)
{
System.out.println(student1.getName() + " is alphabetically greater than " + student2.getName());
}
else
{
System.out.println(student1.getName() + " is the same as " + student2.getName());
}
}
}
The printout from the program above would be
austin has a higher grade than Brock
Brock is alphabetically less than austin
Because the compareTo method in the String class is case-sensitive and the Unicode/ASCII value of an uppercase B (65) is less than the Unicode/ASCII value of a lowercase a (97), "Brock" is alphabetically less than "austin".
Objective #5: Use an interface to take advantage of polymorphism.
- Study the following example where the move method is an example of polymorphism since Bug's and Gecko's move differently. Polymorphism is the concept built into Java that allows a method to work differently for different types of objects. Even though the move method is found in Bug and Gecko below and is spelled the same way in both classes, it works differently in each of those two classes. Polymorphism means "many shapes or many forms".
public interface Movable
{
public void move(int amount);
}
//////////////////////////////////////////////////////////
public class Bug implements Movable
{
private int myX;
public void move(int amount)
{
myX += amount;
}
public void eat()
{
System.out.println("That tasted good!");
}
}
//////////////////////////////////////////////////////////
public class Gecko implements Movable
{
private int myX;
public void move(int amount)
{
myX += 2 * amount;
}
public void swallowWhole()
{
System.out.println("That tasted good!");
}
}
//////////////////////////////////////////////////////////
public class PolymorphismDemo
{
public static void main(String[] args)
{
Bug nemo = new Bug();
Gecko gex = new Gecko();
moveAnything(nemo);
moveAnything(gex);
}
public static void moveAnything(Movable animal)
{
if (animal instanceof Bug)
{
animal.move(10);
}
else if (animal instanceof Gecko)
{
animal.move(8);
}
}
}
- You can use the instanceof operator to determine if an object is of a particular type. The instanceof operator is used above to test what kind of object the parameter animal is. In the method header, animal is a Movable reference. But deep down animal is a reference to either a Bug object or a Gecko object. The instanceof operator can be used to determine which kind of object animal really refers to. The instanceof operator is not tested on the AP exam, but it can be useful especially for safe and flexible programming.
Objective #6: Convert an interface reference to a class type.
- Assuming that the Bug class realizes the Comparable interface, the following statement is legal and compiles
with no errors:
Comparable nemo = new Bug();
But you can also break this statement into two statements by first declaring a reference and then instantiating the object.
Comparable nemo; // declaring nemo as a reference to a Comparable
nemo = new Bug(); // instantiating nemo as a "deep down" Bug object
But the following statements would not compile correctly:
Bug nemo = new Comparable(); // since there is no constructor in the Comparable interface
Comparable nemo = new Comparable(); // since Comparable is an interface & not a class & constructors aren't allowed in interfaces
- The following code segment is legal since you are not instantiating a Comparable interface but only creating the reference and then instantiating
an object later from a class that realizes the interface.
Scanner keyboard = new Scanner(System.in);
int menuChoice = keyboard.nextInt();
Comparable thing;
if (menuChoice == 1)
thing = new Integer(3);
else if (menuChoice == 2)
thing = new Double(4.5);
else if (menuChoice == 3)
thing = "foobar";
or just
if (menuChoice == 1)
Comparable thing = new Integer(3);
else if (menuChoice == 2)
Comparable thing = new Double(4.5);
else if (menuChoice == 3)
Comparable thing = "foobar";
- If you pass a reference to an interface as a parameter
to a method, you can use any method from the interface. But if you want
to use a method that is not in the interface, you must be sure to cast the
reference before using that method.
The following code would cause an error since itemA is
a Comparable reference and the Comparable interface
does not have an implemented move method.
So itemA does not "know" how
to perform the move method.
public static void animateBiggest(Comparable
itemA, Comparable itemB)
{
if (itemA.compareTo(itemB) > 0)
itemA.move(); // error
else
itemB.move(); // error
}
The following code would work since itemA and itemB are
being casted to an actual Bug object that
knows
how
to move itself.
public static void animateBiggest(Comparable itemA,
Comparable
itemB)
{
if (itemA.compareTo(itemB) > 0)
((Bug) itemA).move();
else
((Bug) itemB).move();
}
Notice the extra set of parentheses that are required to make the cast occur before the dot operator. By default, the dot operator has a higher order of precedence in Java.
- Polymorphism is demonstrated in the code segment below. Depending on the value of the variable menuChoice,
one of three different move methods are going to execute. Since the move method is implemented differently in each of the classes Bug, Human, Helicopter,
the move method takes many forms.
public static void playerTurn(Movable object, int menuChoice)
{
if (menuChoice == 1)
((Bug) object).move(90);
else if (menuChoice == 2)
((Human) object).move(90);
else if (menuChoice == 3)
((Helicopter) object).move(90);
}
The compiler can't determine which version of the move method
will execute at compile time since menuChoice depends
on the user's input. So the Java Virtual Machine (JVM) determines which version
executes at run-time. This is called late-binding (also known
as dynamic binding). By the way, early-binding (also known as
static binding) occurs when there are more than one overloaded version of a method
within
a
class
but
the
compiler decides which version of the method will execute by examining the different
parameter lists.
- You can also convert an object into an interface reference as in
String name = "John";
Comparable thing = name;
System.out.println(name.length()); // displays 4
System.out.println(thing.length()); // error since name is a Comparable reference & can't use any String methods
The following statement would be legal though since casting is used to temporarily convert thing to a String
System.out.println(((String) thing).length()); // displays 4
You do not lose any information in the process. That is, the computer does not forget how long the String is.
- You cannot use the following code segment to try to "trick" the compiler into changing an object into another unrelated object that happens to realize the same interface.
String name = "John";
Comparable thing = name;
Integer other = (Integer) thing; // runtime-error exception error
- Note that when casting a double primitive variable to an int, you do lose information (i.e. the decimal places)
double num = 1.234;
int other = (int) num;
System.out.println((double) other); // prints 1 since the .234 are permanently lost
Objective #7: Explain the usefulness of interfaces.
- If it weren't for common, shared interfaces, the World would be a more confusing place. For example, the classes Chevy, Ford, Chrysler, Toyota, Mazda, etc. all share the same
interfaces since they all agree on having the accelerator to the right, the brake on the left, up for turn signal signaling right turn, down for turn signal signaling
left turn, check engine light showing red if something's wrong, headlight switch somewhere to the left of the steering column, steering wheels being round and not a combination of levers, etc. It
would be difficult to drive a rental car that happens to be a Ford, Chevy, or Toyota when your personal car happens to be a Chrysler if all cars didn't share the same basic interface elements.
- If all classes implement the Comparable interface then it makes it easy for client programmers to write and understand code since they know a compareTo method
exists and they can rely on it always returning a positive value in one case, a negative value in other case, or zero in another case. Having interfaces makes it so easy to use other peoples' classes
that you don't waste time reading manuals, documentation, API's, or tracing other people's code. It's like not having to read the car's manual when you want to drive away in the rental car.
- The benefit of interfaces is not that actual code is being reused but that it makes programming easier for future developers. You may not truly understand this benefit until you've
worked on large projects and have more experience writing client code.
- Using interfaces can reduce "coupling" between classes. That is, using interfaces can reduce the need to hard-wire classes together in a way that would force you to use one semi-unrelated class with another one.