330 likes | 462 Views
Java Programming. Abstract classes and Interfaces. Classes. A class is composed of A class defines a data type . . public class Rectangle { private double width, height; public Rectangle(double width, double height) { this.width = width; this.height =height; }
E N D
Java Programming Abstract classes and Interfaces
Classes • A class is composed of • A class defines a data type. public class Rectangle { private double width, height; public Rectangle(double width, double height) { this.width= width; this.height=height; } public double getArea() { return width * height; } public double getPerimeter() { return 2 * (width + height); } public double getAspectRatio() { return width / height; } } data methods
Clients • The class (or type) of an object defines • the data that is managed by the object • the methods we can apply on the object public void processRectangle(Rectangle r) { double x1 = r.getArea(); double x2 = r.getPerimeter(); double x3 = r.getAspectRatio(); String x4 = r.toString(); boolean x5 = r.equals(“Rectangle”); } public class Rectangle { private double width, height; public Rectangle(double width, double height) { this.width= width; this.height=height; } public double getArea() { return width * height; } public double getPerimeter() { return 2 * (width + height); } public double getAspectRatio() { return width / height; } }
Interface • An interface is a collection of method signatures • access control • return type • method name • formal parameters • exceptions • An interface must be implemented to • define the method bodies • define the data
Interface example public interface Shape { public double getArea(); public double getPerimeter(); public double getAspectRatio(); } public class Rectangle implements Shape { private double width, height; public Rectangle(double width, double height) { this.width= width; this.height=height; } public double getArea() { return width * height; } public double getPerimeter() { return 2 * (width + height); } public double getAspectRatio() { return width / height; } } collection of method signatures the methods are fully defined
Interfaces • An interface cannot be instantiated • Shape s = new Shape(); • Shape s = new Rectangle(30, 10); • An implementation must either • define all methods OR • be labeled as abstract • Implementation denotes an is-a relationship • public class Rectangle implements Shape • means that “a Rectangle is-a Shape”
Implement three Shapes • Rectangle • A box with width & height • Isosceles Triangle • A triangle with two equal sides • Ellipse • an oval with major/minor axis height width height width height width
Implementation public interface Shape { public double getArea(); public double getPerimeter(); public double getAspectRatio(); } public class Rectangle implements Shape { private double width, height; public Rectangle(double width, double height) { this.width= width; this.height=height; } public double getArea() { return width * height; } public double getPerimeter() { return 2 * (width + height); } public double getAspectRatio() { return width / height; } } // Constructing and naming a rectangle Shape x = new Rectangle(10,30); height width
Implementation public interface Shape { public double getArea(); public double getPerimeter(); public double getAspectRatio(); } public class IsocelesTriangleimplements Shape { private double width, height; public IsocelesTriangle (double width, double height) { this.width= width; this.height=height; } public double getArea() { return width * height / 2; } public double getPerimeter() { return 2*Math.sqrt(width*width/4 + height*height) + width; } public double getAspectRatio() { return width / height; } } // Constructing and naming a triangle Shape x = new IsocelesTriangle(10,30); height width
Implementation public interface Shape { public double getArea(); public double getPerimeter(); public double getAspectRatio(); } public class Ellipse implements Shape { private double width, height; public Ellipse(double width, double height) { this.width= width; this.height=height; } public double getArea() { return Math.PI * width * height / 4; } public double getPerimeter() { double a= width/2, b = height/2; double x = Math.max(a,b), y = Math.min(a,b); int digits = 53; double tolerance = Math.sqrt(Math.pow(a, digits)); double s = 0, m = 1; while(x-y>tolerance*y) { double y1 = Math.sqrt(x*y); double x1 = (x+y)/2; x = x1; y = y1; m *= 2; s += m * Math.pow(x-y,2); } return Math.PI * (Math.pow(a+b, 2)-s)/(x+y); } public double getAspectRatio() { return width / height; } } // Constructing and naming an ellipse Shape x = new Ellipse(10,30); height width
Abstract Class public class IsocelesTriangleimplements Shape { private double width, height; public IsocelesTriangle (double width, double height) { this.width= width; this.height=height; } public double getArea() { return width * height / 2; } public double getPerimeter() { return 2*Math.sqrt(width*width/4+height*height)+width; } public double getAspectRatio() { return width / height; } } public class Rectangle implements Shape { private double width, height; public Rectangle(double width, double height) { this.width= width; this.height=height; } public double getArea() { return width * height; } public double getPerimeter() { return 2 * (width + height); } public double getAspectRatio() { return width / height; } } public class Ellipse implements Shape { private double width, height; public Ellipse(double width, double height) { this.width= width; this.height=height; } public double getArea() { return Math.PI * width * height / 4; } public double getPerimeter() { double a= width/2, b = height/2; double x = Math.max(a,b), y = Math.min(a,b); int digits = 53; double tolerance = Math.sqrt(Math.pow(a, digits)); double s = 0, m = 1; while(x-y>tolerance*y) { double y1 = Math.sqrt(x*y); double x1 = (x+y)/2; x = x1; y = y1; m *= 2; s += m * Math.pow(x-y,2); } return Math.PI * (Math.pow(a+b, 2)-s)/(x+y); } public double getAspectRatio() { return width / height; } } Notice the similar code in these three implementations. Should aggregate into an abstract class.
Abstract Class public abstract class AbstractShapeimplements Shape { protected double width, height; public AbstractShape(double width, double height) { this.width= width; this.height=height; } public double getAspectRatio() { return width / height; } } public class IsocelesTriangleextends AbstractShape{ public IsocelesTriangle (double width, double height) { super(width,height); } public double getArea() { return width * height / 2; } public double getPerimeter() { return 2*Math.sqrt(width*width/4+height*height)+width; } } public class Rectangle extends AbstractShape{ public Rectangle(double width, double height) { super(width, height); } public double getArea() { return width * height; } public double getPerimeter() { return 2 * (width + height); } } public class Ellipse extends AbstractShape{ public Ellipse(double width, double height) { super(width, height); } public double getArea() { return Math.PI * width * height / 4; } public double getPerimeter() { double a= width/2, b = height/2; double x = Math.max(a,b), y = Math.min(a,b); int digits = 53; double tolerance = Math.sqrt(Math.pow(a, digits)); double s = 0, m = 1; while(x-y>tolerance*y) { double y1 = Math.sqrt(x*y); double x1 = (x+y)/2; x = x1; y = y1; m *= 2; s += m * Math.pow(x-y,2); } return Math.PI * (Math.pow(a+b, 2)-s)/(x+y); } }
Abstract Class • An abstract class • is not completely defined • may have methods • may have data • will usually have a collection of method signatures • cannot be instantiated • AbstractShape s = new AbstractShape(3, 5); • AbstractShape s = new Rectangle(3, 5);
public interface Shape { public double getArea(); public double getPerimeter(); public double getAspectRatio(); } public class ShapeDriver { public static Shape getRandomShape() { double width = Math.random() * 20; double height = Math.random() * 20; int type = (int) (Math.random() * 3); switch (type) { case 0: return new Ellipse(width, height); case 1: return new IsocelesTriangle(width, height); case 2: return new Rectangle(width, height); } throw new IllegalArgumentException(); } public static double totalArea(List<Shape> shapes) { double totalArea = 0; for(Shape s : shapes) { totalArea += s.getArea(); } return totalArea; } public static void main(String[] args) { List<Shape> shapes = new LinkedList<>(); for (inti = 0; i < 20; i++) { shapes.add(getRandomShape()); } System.out.println(totalArea(shapes)); } }
Generics • A generic class is a class that is parameterized over types. T1 and T2 are type parameters. public class Pair<T1,T2> { private T1 first; private T2 second; public Pair(T1 first, T2 second) { this.first = first; this.second = second; } public T1 getFirst() { return first; } public T2 getSecond() { return second; } public void setFirst(T1 first) { this.first = first; } public void setSecond(T2 second) { this.second = second; } } Type arguments are supplied when the class is used. public class PairDriver { public static void main(String[] args) { Pair<String, Integer> p1 = new Pair<>(“Kenny”, 1); Pair<Integer, Double> p2 = new Pair<>(2, 3.5); String x1 = p1.getFirst(); Integer x2 = p1.getSecond(); Integer x3 = p2.getFirst(); Double x4 = p2.getSecond(); Pair<Pair<String, Integer>,Pair<Integer, Double>> p3 = new Pair<>(p1, p2); ??? x5 = p3.getFirst(); ??? x6 = p3.getSecond(); } }
Comparable & Comparator public interface Comparable<T> { public intcompareTo(T e); } • Java has two commonly used interfaces • Comparable<T> • This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class's natural ordering. • The compareTo method this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object. • Comparator<T> • A comparison function, which imposes a total ordering on some collection of objects. • The compare method compares its two arguments for order. Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second. public interface Comparator<T> { public intcompare(T e1, T e2); }
Example • Can the AbstractShape class implement the Comparable interface? • We define the natural ordering of shapes by keying on the area. public abstract class AbstractShapeimplementsShape, Comparable<Shape>{ protected double width, height; public AbstractShape(double width, double height) { this.width = width; this.height = height; } public double getAspectRatio() { return width / height; } public intcompareTo(Shape e) { double diff = getArea() – e.getArea(); if(diff < 0) { return -1; } else if(diff > 0) { return 1; } else { return 0; } } }
Why the interface? public static AbstractShapegetSmallest(AbstractShape[] shapes) { if(shapes.length == 0) throw new NoSuchElementException(); AbstractShapesmallest = shapes[0]; for(inti=1; i<shapes.length; i++){ if(shapes[i].compareTo(smallest) < 0) { smallest = shapes[i]; } } return smallest; } Could write code like this. It finds the smallest shape in an array of AbstractShapes. public static void example() { AbstractShape[] shapes = new AbstractShape[10]; for(inti=0; i<10; i++) { shapes[i] = getRandomShape(); } AbstractShape smallest = getSmallest(shapes); }
Why the interface? public static ComparablegetSmallest(Comparable[] items) { if(items.length == 0) throw new NoSuchElementException(); Comparable smallest = items[0]; for(inti=1; i<items.length; i++){ if(items[i].compareTo(smallest) < 0) { smallest = items[i]; } } return smallest; } Better to write code like this. It finds the smallest “thing” in an array of “things”. public static void example() { AbstractShape[] shapes = new AbstractShape[10]; for(inti=0; i<10; i++) { shapes[i] = getRandomShape(); } AbstractShape smallest = getSmallest(shapes); }
Generic Methods • Methods can introduce type parameters Generic type parameter String s = randomChoice(“a”, “b”); Double x = randomChoice(1.0, 2.3); Integer y = randomChoice(3,5); Shape u = new Rectangle(10,30); Shape v = new Rectangle(30, 50); Shape t = randomChoice(u, v); public <T> T randomChoice(T x1, T x2) { if(Math.random() < .5) { return x1; } else { return x2; } }
Generic Methods • Can we write a generic method to accept to elements of some type and return the smallest element? public <T> T smallest(T x1, T x2) { if(x1 < x2) { return x1; } else { return x2; } } This doesn’t work since the “<“ operator is not supported on object types. public <T> T smallest(T x1, T x2) { if(x1.compareTo(x2) < 0) { return x1; } else { return x2; } } This doesn’t work since the “compareTo” method is not supported on objects that don’t implement Comparable.
Generic Methods • Can we write a generic method to accept elements of some type and return the smallest element? • Notation to put an upper-bound on a methods generic parameter • TYPENAME extends UPPERBOUND • Examples: • <T extends JPanel> • <T extends Comparable<T>> • <T extends JComponent> public <T extends Comparable<T>> T smallest(T x1, T x2) { if(x1.compareTo(x2) < 0) { return x1; } else { return x2; } } This works since T has an “upper bound” of Comparable<T>. This means that whatever T is, it is a sub-class of Comparable<T>. String x1 = smallest(“a”, “b”); Integer x2 = smallest(15, 3); Double x3 = smallest(2, -18);
Generics and Subtyping • Consider the following example. What are the conformance rules for generic classes? Pair<Object, Object> p1 = new Pair<Object,Object>(“a”, “b”); p1.setFirst(4); // IS THIS VALID? p1.setSecond(“c”); // IS THIS VALID? Pair<String, Integer> p2 = new Pair<String, Integer>(“a”, 3); p2.setFirst(4); // IS THIS VALID? p2.setSecond(“c”); // IS THIS VALID? p1 = p2; // IS THIS VALID? p1.setFirst(4); p1.setSecond(“c”);
Generics and Conformance • Conformance rules • If A is a non-generic super-class of B then objects of type B conform to A • Shape s = new Rectangle(10,30); • Number x = new Double(3.5); • If A is a generic super-class of B, then objects of B type conform to A only if each generic parameter is an exact match. • List<Shape> x = new LinkedList<Rectangle>; • List<Shape> y = new LinkedList<Shape>;
Bounded Type Parameters • When a method declares a parameterized type, the actual parameters must match exactly. public Object pickOne(TwoOfAKind<Object> pair) { if(Math.random() < . 5) { return pair.getFirst(); } else { return pair.getSecond(); } } public class TwoOfAKind<T> { private T first; private T second; public TwoOfAKind (T first, T second) { this.first = first; this.second = second; } public T getFirst() { return first; } public T getSecond() { return second; } public void setFirst(T first) { this.first = first; } public void setSecond(T second) { this.second = second; } } TwoOfAKind<String> p1 = new TwoOfAKind<String>(“a”, “b”); TwoOfAKind<Object> p2 = new TwoOfAKind<Object>(1, ”c”); Object x = pickOne(p1); Object y = pickOne(p2);
Generics and Wildcards • Wildcards allow us to write truly generic functions. • ? denotes ANY TYPE public Object pickOne(TwoOfAKind<?> pair) { if(Math.random() < . 5) { return pair.getFirst(); } else { return pair.getSecond(); } } TwoOfAKind<String> p1 = new TwoOfAKind<String>(“a”, “b”); TwoOfAKind<Object> p2 = new TwoOfAKind<Object>(1, ”c”); Object x = pickOne(p1); Object y = pickOne(p2);
Generics and Wildcards • The wildcard can be constrained. If A is the name of some class then • ? extends A • the ? stands for some class that is either class A or a SUB CLASS OF A • A is an upper-bound public Comparable pickOne(TwoOfAKind<? extends Comparable> pair) { if(Math.random() < . 5) { return pair.getFirst(); } else { return pair.getSecond(); } } TwoOfAKind<String> p1 = new TwoOfAKind<String>(“a”, “b”); TwoOfAKind<Object> p2 = new TwoOfAKind<Object>(1, ”c”); Object x = pickOne(p1); Object y = pickOne(p2);
Generics and Wildcards • The wildcard can be constrained. If A is the name of some class then • ? super A • the ? stands for some class that is either class A OR A SUPER CLASS OF A • A is a lower-bound public Object pickOne(TwoOfAKind<? super Integer> pair) { if(Math.random() < . 5) { return pair.getFirst(); } else { return pair.getSecond(); } } TwoOfAKind<String> p1 = new TwoOfAKind<String>(“a”, “b”); TwoOfAKind<Number> p2 = new TwoOfAKind<Number>(1, 3.5); Object x = pickOne(p1); Object y = pickOne(p2);
Generic Interface Example public interface Function<X,Y> { public Y apply(X x); } The interface describes one function public class Square implements Function<Double, Double> { public Double apply(Double x) { return x * x; } } public class isEven implements Function<Integer, Boolean> { public Boolean apply(Integer x) { return x % 2 == 0; } } Each of these non-abstract classes defines that function. public class Redness implements Function<Color, Integer> { public Integer apply(Color color) { return color.getRed(); } }
Roots • The root of a function f(x) is the value of x such that f(x) = 0. • A function may have multiple roots. • Problem: given a continuous function f and interval [a, b], find a root in [a, b] if one exists. • Find a root of f(x) = x*x - 1 in [0, 10]. • x = 1 • Find a root of f(x) = sin(x) in [-Pi,Pi]. • x = -Pi, Pi, 0
Finding the root of a function public static double root(Function<Double, Double> f, double a, double b, double tol) { int NMAX = 50; int n = 1; while(n <= NMAX) { double c = (a + b)/2; double fc = f.apply(c); if(fc == 0 || (b-a)/2 < tol) { return c; } n = n + 1; if(Math.signum(fc) == Math.signum(f.apply(a))) { a = c; } else { b = c; } } throw new IllegalArgumentException(); }
Finding the root of a function public static double squareRoot(final double value) { return root( new Function<Double,Double>() { public Double apply(Double x) { return x * x - value; } }, 0, Math.max(value, 1), 1e-12); }