180 likes | 269 Views
Generics. Example. Stack<E> + Stack<E> () + boolean isEmpty () + void push (E z) + E pop () + E peek (). Stack<String> s1 , s2 ; String str ; s1 = new Stack<String>(); s1.push( “ humble ” ); s1.push( “ bumble ” ); s1.push( “ mumble ” ); s1.pop();
E N D
Example Stack<E> + Stack<E>() + boolean isEmpty() + void push(E z) + E pop() + E peek() Stack<String> s1, s2; String str; s1 = new Stack<String>(); s1.push( “humble” ); s1.push( “bumble” ); s1.push( “mumble” ); s1.pop(); s1.push( “tumble” ); str = (String) s1.peek(); s1.push( “rumble” ); s1.push( “crumble” ); s1.pop(); s1.pop(); s1.push( s1.peek() ); s2 = new Stack<String> (); while ( !s1.isEmpty() ) { s2.push( s1.peek() ); s1.pop(); }
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 int compare(T e1, T e2); }
Interface Shape public interface Shape { public double getArea(); public double getPerimeter(); public double getAspectRatio(); } 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; } }
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 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 void example() { AbstractShape[] shapes = new AbstractShape[10]; for(inti=0; i<10; i++) { shapes[i] = getRandomShape(); } AbstractShape smallest = getSmallest(shapes); }
Why the interface? public 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 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; } } Each of these non-abstract classes defines that function. 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(); } }