Interfaces, Abstract Classes, and Polymorphism What Is an Interface? • An interface is the set of public methods in a class • Java provides the syntax for specifying interfaces independently of their implementing classes Example: An Interface for Circle import java.awt.Graphics; public interface CircleInterface { public public public public void draw(Graphics g); void move(int xDistance, int yDistance); boolean equals(Object other); String toString(); } An interface contains public method signatures and any necessary imports Implementing an Interface import java.awt.*; public class Circle implements CircleInterface { // Variables and methods } Circle must implement draw, move, equals, and toString import java.awt.*; public class Wheel extends Circle { // Variables and methods } The Client’s Perspective Circle c1 = new Circle(10,10,100,100,Color.blue); CircleInterface c2 = new Circle(); CircleInterface[] list = new CircleInterface[25]; Clients just look at the interface to see what they can do with circles An interface name can be substituted for the name of any implementing class Implementing Several Interfaces import java.awt.*; import java.io.Serializable; public class Circle implements CircleInterface, Cloneable, Comparable, Serializable { // Variables and methods } Circle must implement draw, move, equals, and toString Circle must also implement clone and compareTo Abstract and Concrete Classes • An abstract class defines data and methods common to all subclasses, but is never instantiated • A concrete class inherits some data and methods, defines others, and is instantiated Example: Other Shapes Rectangles and triangles are not really special types of circles, but have similar attributes and behavior Rectangle Circle Wheel Triangle Define an AbstractShape Class AbstractShape is never instantiated, but AbstractShape simply holds data and methods for its subclasses Rectangle Circle Wheel Each shape has a position, size, and color Triangle Most shapes move in a similar manner An Interface for All Shapes import java.awt.Graphics; public interface Shape { public public public public } void draw(Graphics g); void move(int xDistance, int yDistance); boolean equals(Object other); String toString(); Defining an Abstract Class abstract public class AbstractShape implements Shape{ protected Color color; protected int x, y, width, height; // Methods go here } import java.awt.*; public class Circle extends AbstractShape { // Methods go here } abstract means that the class is not instantiated Polymorphism • The move, draw, toString, and equals methods are polymorphic, in that they’re included in all classes but are implemented differently in some of them • Try to exploit polymorphism to – reduce the amount of redundant code – reduce the conceptual overhead for clients Abstract Methods // In the AbstractShape class abstract public void draw(Graphics g); The keyword abstract requires all subclasses to implement a method An abstract method is implemented differently in each subclass When a Method Can’t Be Supported // In the AbstractShape class abstract public double getArea(); // In the Rectangle class public double getArea(){ return width * height; } // In the Line class public double getArea(){ throw new UnsupportedOperationException("Lines have no area"); return 0; } Overriding a Method // In the AbstractShape class public void move(int xDistance, int yDistance) x += xDistance; y += yDistance; } // In the Triangle class public void move(int xDistance, int yDistance) super.move(xDistance, yDistance); // Code to adjust the other vertices goes here } Final Methods // In the AbstractShape class public final void setColor(Color c){ color = c; } The keyword final prohibits any subclass from overriding a method. Types of Polymorphism • Behavioral – several classes implement one interface • Structural – several classes implement one abstract class The Advantages of Polymorphism // Create an array to hold up to 3 shapes of any type Shape[] shapes = new Shape[3]; // Add several shapes to the array shapes[0] = new Circle(50,50,100,100, Color.red); shapes[1] = new Rectangle(0,0,50,50, Color.blue); shapes[2] = new Triangle(0,0,100,100,60,30, Color.red); // Move them and draw them all for (int i = 0; i < shapes.length; i++){ shapes[i].move(50, 50); shapes[i].draw(g); } Watch Out for Type Mismatches // Set the number of spokes of all wheels to 6 for (int i = 0; i < shapes.length; i++) shapes[i].setSpokes(6); Causes a compile-time error, because setSpokes is not included in the Shape interface and the array’s element type is Shape Casting to the Rescue // Set the number of spokes of all wheels to 6 for (int i = 0; i < shapes.length; i++){ Wheel w = (Wheel)shapes[i]; w.setSpokes(6); } The compiler is now happy, but if shapes[i] is not actually a Wheel at run time, a ClassCastException will be thrown Ask First // Set the number of spokes of all wheels to 6 for (int i = 0; i < shapes.length; i++){ if (shapes[i] instanceof Wheel){ Wheel w = (Wheel)shapes[i]; w.setSpokes(6); } } Now both the compiler and the JVM will be happy For Friday • Exceptions and error handling • Serialization (file transfers)