Wyo Java Ch. 9 Lecture Notes
Note: Inner classes as explained on pp. 379 & timer
events as explained on 380-386 will not be covered in this course and are not
covered on the AP exam.
Objective #1: Understand how to use interfaces.
- An interface is a list of method signatures that establish behavior that unrelated classes can share. The methods must be public, however the keyword public should not be used. An interface cannot contain the implementation (i.e. the body inside of the set of curly braces) of any of the methods that it lists.
Here is an example of an interface named Movable. I made up this interface as a simple example; it is not a famous interface provided by Sun or our textbook.
public interface Movable
{
void move(int amount);
}
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. A method is called an abstract method when it has no implementation.
- An interface cannot contain any instance variables. An interface cannot contain any constructors either. An interface may contain constants though. As we have already learned a constant in Java is simply a field with public static final typed at the front of its declaration statement. But since all variables in an interface are automatically considered to be public static final, these keywords should not be typed in a constant's declaration statement. Here is an example of the Movable interface with a constant named SPEED_OF_LIGHT:
public interface Movable
{
static final int SPEED_OF_LIGHT = 3e8;
void move(int amount);
}
- Classes can "realize" an interface. Realizing an interface is also called implementing an interface. To realize an interface, a class must fully implement each of the methods listed in the interface. An unlimited number of unrelated classes can implement the same interface. Each of these unrelated classes may implement the methods in the shared interface in different ways.
- Use the keyword implements to make a class realize an interface as in
public class Fish implements Movable
where the class Fish realizes the Movable interface
or
public class BigNumbers implements Comparable
where the class BigNumbers realizes the Comparable interface.
- More than one interface can be realized by a class as in
public class Fish implements Movable, Comparable
where both interfaces Movable and Comparable are realized by the Fish class. A comma is used to separate the interfaces.
- Our textbook uses the Measurable interface as an example of an interface. The Coin class and the BankAccount class are unrelated but they both realize the Measurable interface. The Measurable interface only includes one method named getMeasure. Read the textbook closeley to see how the Coin and BankAccount classes implement the getMeasure method differently.
- Using interfaces can reduce coupling between classes.
- Example
public interface Movable
{
static final int SPEED_OF_LIGHT = 3e8;
void move(int amount);
}
public class Fish implements Movable
{
private int myX;
public void move(int amount)
{
myX += amount;
}
public void eatFishBait()
{
System.out.println("Whoops");
}
}
public class Ch9Demo
{
public static void main(String[] args)
{
Fish nemo = new Fish();
nemo.move(5);
System.out.println("Fish cannot move faster than " + Fish.SPEED_OF_LIGHT);
nemo.eatFishBait();
Movable willy = new Fish();
willy.move(5);
willy.eatFishBait(); // illegal since eatFishBait isn't in Movable interface
}
}
Objective #2: Use the Comparable interface and override its compareTo method.
- Sun's interface java.lang.Comparable is
used by Java programmers to be able to compare objects. The Comparable interface
includes only one method and looks like this:
public interface Comparable
{
int compareTo(Object other);
}
however you would never need to type out this code since it's already compiled and stored as part of the Java language.
- If a class such as Integer, String, or Rectangle implements the Comparable interface then the compareTo method must be implemented in that class. When implementing the compareTo method, a programmer must decide how he/she wants to compare two objects of a given class and what would make one object less than or greater than another object. 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.
- For example, if you had a Student class with gpa, last name, and height 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
public int compareTo(Object explParam)
{
if (this.myGPA > ((Student) explParam).myGPA)
return 1;
else if (this.myGPA < ((Student) explParam).myGPA)
return -1;
return 0;
}
or you could override it like this
public int compareTo(Object explParam)
{
return this.myName.compareTo(((Student) explParam).myName);
}
or you could even override it like this
public int compareTo(Object explParam)
{
if (myGPA + myHeight > ((Student) explParam).myGPA + ((Student) explParam).myHeight)
return 1;
else if (myGPA + myHeight < ((Student) explParam.)myGPA + ((Student) explParam).myHeight)
return -1;
return 0;
}
Observe that the use of the this operator is optional in the examples above. But it is necessary to cast the explicit parameter named explParam to a Student object since explParam is passed as an Object.
- When overriding the compareTo method, the exact method header
public int compareTo(Object explParam)
must be used since this is the method header found in the Comparable interface. Any parameter name can be substituted for explParam though. When overriding a method from an interface, the method must be public. It cannot be private.
- 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 #3: Use an interface to take advantage of polymorphism.
- Study the following example where the move method is an example of polymorphism since fish and geckos move differently.
public interface Movable
{
void move(int x);
}
public class Fish implements Movable
{
private int myX;
public void move(int x)
{
myX += x;
}
public void eat()
{
System.out.println("That tasted good!");
}
}
public class Gecko implements Movable
{
private int myX;
public void move(int x)
{
myX += 2 * x;
}
public void swallowWhole()
{
System.out.println("That tasted good!");
}
}
public class Ch9DemoPolymorphism
{
public static void main(String[] args)
{
Fish nemo = new Fish();
Gecko gex = new Gecko();
moveAnything(nemo);
}
public static void moveAnything(Movable x)
{
if (x instanceof Fish)
{
x.move(10);
}
else if (x instanceof Gecko)
{
x.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 x is. In the method header, x is a Movable reference. But deep down x is a reference to either a Fish object or a Gecko object. The instanceof operator can be used to determine which kind of object x 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 #4: Convert an interface reference to a class type.
- Assuming that the Fish class realizes the Comparable interface, the following statements are legal and compile with no errors:
Comparable nemo = new Fish();
or
Comparable nemo;
nemo = new Fish();
But the following statements would not compile correctly:
Fish nemo = new Comparable(): // since there is no constructor in the Comparable interface
Comparable nemo = new Comparable(); // since there is no constructor in the Comparable interface
- In a client program, you are allowed to declare a reference to an interface. For example,
Comparable thing;
is legal since thing is only a reference and not an instantiated object. You would never see the statement
Comparable thing = new Comparable();
since Comparable is not a class and does not have a default constructor. In other words, you cannot instantiate an object from an interface.
- The following code segment is legal since you are not instantiating a Comparable interface but only creating the reference and then instantiating an object from a class that realizes the interface.
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 include an interface reference 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();
else
itemB.move();
}
The following code would work since itemA and itemB are
being casted to an actual Fish object that
knows
how
to move itself.
public static void animateBiggest(Comparable itemA,
Comparable
itemB)
{
if (itemA.compareTo(itemB) > 0)
((Fish) itemA).move();
else
((Fish) 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.
- An object-oriented programming concept called 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 Fish, Human, Helicopter, the move method takes many forms. Polymorphism means "many shapes or many forms".
public static void playerTurn(Movable object)
{
if (menuChoice == 1)
((Fish) 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;
but you cannot follow the two lines of code given above with the statement
int len = name.length();
since name is a Comparable reference and can no longer use any String methods unless it is casted to a String. The following statement would be legal
int len = ((String) name).length();
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;
As in the following example, you can convert an object to an interface and then cast it back to the original type of object. You do not lose any information in the process:
String name = "John";
Comparable thing = name;
System.out.println((String) thing.length()); // prints 4
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