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