33
Java Programming Abstract classes and Interfaces

Java Programming Abstract classes and Interfaces

Embed Size (px)

Citation preview

Java ProgrammingAbstract 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();}

collection of method signatures

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; }}

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

width

heig

ht

width

heig

ht

width

heig

ht

Implementation

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 interface Shape { public double getArea(); public double getPerimeter(); public double getAspectRatio();}

width

heig

ht

// Constructing and naming a rectangleShape x = new Rectangle(10,30);

Implementation

public interface Shape { public double getArea(); public double getPerimeter(); public double getAspectRatio();}

public class IsocelesTriangle implements 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; }}

width

heig

ht// Constructing and naming a triangleShape x = new IsocelesTriangle(10,30);

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; }}

width

heig

ht

// Constructing and naming an ellipseShape x = new Ellipse(10,30);

Abstract Class

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 IsocelesTriangle implements 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 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 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 IsocelesTriangle extends 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 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); }}

public abstract class AbstractShape implements Shape { protected double width, height;

public AbstractShape(double width, double height) { this.width= width; this.height=height; }

public double getAspectRatio() { return width / height; }}

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 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 (int i = 0; i < 20; i++) { shapes.add(getRandomShape()); } System.out.println(totalArea(shapes)); }}

public interface Shape { public double getArea(); public double getPerimeter(); public double getAspectRatio();}

Generics

• A generic class is a class that is parameterized over types.

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; }}

T1 and T2 are type parameters.

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(); }}

Type arguments are supplied when the class is used.

Comparable & Comparator

• 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 Comparable<T> { public int compareTo(T e);}

public interface Comparator<T> { public int compare(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 AbstractShape implements Shape, 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 int compareTo(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 AbstractShape getSmallest(AbstractShape[] shapes) { if(shapes.length == 0) throw new NoSuchElementException(); AbstractShape smallest = shapes[0]; for(int i=1; i<shapes.length; i++){ if(shapes[i].compareTo(smallest) < 0) { smallest = shapes[i]; } } return smallest; }

public static void example() { AbstractShape[] shapes = new AbstractShape[10]; for(int i=0; i<10; i++) { shapes[i] = getRandomShape(); } AbstractShape smallest = getSmallest(shapes);}

Could write code like this. It finds the smallest shape in an array of AbstractShapes.

Why the interface?

public static Comparable getSmallest(Comparable[] items) { if(items.length == 0) throw new NoSuchElementException(); Comparable smallest = items[0]; for(int i=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(int i=0; i<10; i++) { shapes[i] = getRandomShape(); } AbstractShape smallest = getSmallest(shapes);}

Generic Methods

• Methods can introduce type parameters

public <T> T randomChoice(T x1, T x2) { if(Math.random() < .5) { return x1; } else { return x2; }}

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);

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; }}

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.

This doesn’t work since the “<“ operator is not supported on object types.

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); }

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; }}

public class Redness implements Function<Color, Integer> { public Integer apply(Color color) { return color.getRed(); }}

The interface describes one function

Each of these non-abstract classes defines that function.

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

Bisection method

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); }