Abstract classes If two modules B and C share methods and/or method headers and/or instance variables, then abstract classes allow us to avoid repeating common code in the definitions of B and C. The technique is to first write the common components in a stripped-down or abstract class A: abstract ... ... ... ... } class A { instance variables ... constructors ... methods ... method headers (each beginning with abstract)... Then write class B as an extension of A, and similarly for C: class B extends A { // Note A’s components automatically included. ... complete code for A’s abstract methods... ... and any extra components ... } Classes that are not abstract (which is most classes) are said to be concrete. Example: avoiding code duplication in class definitions abstract class Shape { private String name; // will occur in all extensions Shape(String name0) {name = name0;} abstract double area(); // no body, note abstract abstract double perim(); void put() { // will occur in all extensions System.out.println(name + " with area " + area()+ " and perimeter " + perim()); // ok to use methods whose bodies not yet written } } class Rectangle extends Shape { private double width, height; // & name inherited Rectangle(String s, double w, double h) { super(s); // use Shape constructor; effect is name=s; width = w; height = h; Abstract Classes 1 } double area() {return(width*height);} // body added double perim() {return((width+height)*2);} // body added } class Triangle extends Shape { private double a, b, c; // lengths of sides Triangle(String s, double x, double y, double z) { super(s); a = x; b = y; c = z; } double area() { double s = (a+b+c)/2.0; return Math.sqrt(s*(s-a)*(s-b)*(s-c)); } double perim() {return(a+b+c);} } Abstract classes vs Inheritance The roles of abstract classes and class inheritance are similar. While you may have a choice as to which technique you use, usually just one is applicable in a given context. For example, we could not construct Shape above as a class from which Rectangle and Triangle inherit, because Shape would not then be able to include put (if you don’t see why, try it!). Abstract classes and polymorphism Abstract classes encourage generic coding by allowing variables to be declared to be of type A where A is an abstract class, and to be assigned objects of any class that extends A, e.g. Shape s1 = new Rectangle("Rectangle",2.0,3.0); Shape s2 = new Triangle("Scalene",2.0,3.0,4.0); Shape[] sn = {new Rectangle("Rectangle",2.0,3.0), new Triangle("Scalene",2.0,3.0,4.0), ...} Again, the rule of polymorphism applies: wherever a value of abstract class A is specified, we may supply an object of a type B that extends A (but we may only refer to the components of A). Example: writing common code class Shapes { Abstract Classes 2 private static Shape biggest(Shape[] w) { // Note param type // biggest shape in w[0..] according to areas, w not empty Shape max = w[0]; // note type for (int i=1; i<w.length; i++) { if (w[i].area()>(max.area())) max = w[i]; } return max; } public static void main(String args[]) { Rectangle[] rs = {new Rectangle("Rectangle",2.0,3.0), new Rectangle("Square",4.0,4.0)}; biggest(rs).put(); Shape[] figs = {new Rectangle("Rectangle",2.0,3.0), new Triangle("Scalene",5.0,12.0,13.0), new Rectangle("Square",4.0,4.0)}; biggest(figs).put(); } } Square with area 16.0 and circumference 16.0 Scalene with area 30.0 and circumference 30.0 Technical note: biggest(rs) above returns a Rectangle, but the compiler treats it as of type Shape as that is the return type of biggest. Nevertheless, biggest(rs).put() is valid as put is a method in Shape. Overriding (same-naming) applies just as with inheritance (e.g. Triangle could provide its own put method to override the put in Shape). Multilevel inheritance Classes defined by extension can in turn be extended class Equilateral extends Triangle { Equilateral(String s, double x) { super(s,x,x,x); } } Abstract classes in libraries Libraries that are designed to facilitate application programmers typically include many abstract classes. The abstract classes contain general code that is useful in many contexts, but which omit details that are peculiar to each particular context. The programmer uses abstract classes (which are in compiled form, note) by extending them to create a concrete class with the missing details for the context he has in mind. Abstract Classes 3