So we didn't say the complete truth about classes when we said they contained method signatures. It made it easier to explain classes by going through a two step process.
However, it turns out, there are entities like classes which do have method signatures. They're called interfaces.
So what's the point? Why have interfaces?
Suppose you have some drawing program, and for some reason, you need all shapes to be able to support a getArea() method and a getPerimeter() method. You don't particularly care what kind of classes you have. In fact, you might have a Triangle class, a Rectangle class, and a Polygon class which all have these methods.
How could you work with objects of any of these types, if you want to use getArea() and getPerimeter().
public interface Drawable
{
public int getArea() ;
public int getPerimeter() ;
}
This should go in a file called Drawable.java.
We put public in front of the method signatures. It doesn't really make sense to put private. private is meant for internal use, and class designers should be allowed to pick private methods.
Interfaces are meant for public methods from the perspective of the object user.
Think of it as a list of requirements that a class must have. Let's see an example of this.
public class Rectangle implements Drawable
{
// CODE GOES HERE
}
Notice that we added implements Drawable at the end of
the public class Rectangle.
This means that the class is attempting to implement all methods listed in the Drawable interface. When Java compile the code, it looks at the interface, and sees if all methods listed in there are listed in your class. If it's not, then there's a compiler error.
Let's say, for example, that we have Ellipse, Triangle, and Trapezoid. Each of them also implements the Drawable interface.
The real magic is this: we can treat Drawable like a type. Let's see how.
public class FooTest
{
public static void main( String [] args )
{
Drawable d1 = new Rectangle( 2, 3 ) ;
Drawable d2 = new Triangle( 4, 5 ) ;
Drawable d3 = new Ellipse( 3, 8 ) ;
Drawable d4 = new Trapezoid( 3, 8, 10 ) ;
System.out.println( "Rect area: " + d1.getArea() ) ;
System.out.println( "Triangle area: " + d2.getArea() ) ;
System.out.println( "Ellipse area: " + d3.getArea() ) ;
System.out.println( "Trapezoid area: " + d4.getArea() ) ;
}
}
Let's look at the first unusual statement.
Drawable d1 = new Rectangle( 2, 3 ) ;We're assigning a Rectangle object handle to a Drawable variable. It is not a Drawable object, because Drawable is not a class. It's an interface.
Yet Java permits this. You can assign an object to an interface variable provided the type of the object implements the interface. In this case, since Rectangle implements Drawable we can make this assignment.
The Java compiler checks to make sure each assignment is valid by checking to see if each object's type implements the Drawable interface.
Although d1 has a handle to a Rectangle object, d1 has a declared type of Drawable. If you use d1, you can only use Drawable methods. For example, you can no longer directly use getWidth() or getHeight().
The rule of thumb: you can only use interface methods with an interface variable.
Notice how powerful interfaces are. We can get the area of many different types and treat them as if they were from the same class, even though they aren't.
We can do an operation called dynamic casting.
Drawable d1 = new Rectangle( 2, 3 ) ; Rectangle rect = (Rectangle) d1 ;(Rectangle) is a dynamic cast. You put parentheses around the class. While the program is running, the JVM looks at the actual type of d1. The JVM checks to see if d1 holds a handle to a Rectangle object. If it does, the cast succeeds, and we can use rect, which now has a handle to the same Rectangle object, but we can now call all the methods of Rectangle.
The cast could fail. Perhaps d1 has a handle to a Triangle object. In that case, when the JVM is checking to see whether d1 has a handle to a Rectangle, it fails.
This creates an error, and typically causes your program to end. This error is called an exception. There are ways to deal with the error. However, we'll talk about that later on when we explain how exceptions work.
This allows us flexibility later on. If we want to change the behavior (say, use an Ellipse instead of a Rectangle), then we write a class that implements the interface, create an interface variable, and as long as most of the code uses the interface variable, we're set. We have to make no new changes.
Just to give you an idea of the power of the idea, imagine you've written a class and you've used a lot of Rectangle objects. Say, it appears 1000 times. But now you want to use Ellipse instead. Now you have to search and replace 1000 times.
If we had used Drawable throughout (assuming that only getArea() and getPerimeter() are the only methods used), then we would have only to initialize the Drawable variable to an Ellipse object, and the change would be very simple.
You write the header of the class definition as:
public class Rectangle implements Drawable, Comparable
{
}
You must implement all methods in both these interfaces. You
can then declare either a Drawable variable or a Comparable
variable and assign it to a Rectangle object.