Föreläsning 3 Generiska klasser och metoder (Weiss kap. 4.7-8, Skansholm 17.1) Generiska <klasser> <Generiska> klassmetoder Råa typer Typbegränsningsuttryck Jämförelseklasser Definition av generisk klass public class klassnamn <typuttryck 1, typuttryck 2, > // metoder och variabler ; Typvariablerna i typuttrycken får användas för att sätta typ på variabler i konstruktorer och metoder: parametrar, lokala variabler, returtyper (dock ej i statiska klassmetoder) En generisk klass kan ha flera olika typvariabler typvariabelnamnen kan väljas fritt (men inled med stor bokstav) Även interface kan vara generiska 1 2 Instansiering av generisk klass Klassnamn <typ 1, typ 2, > objektnamn; Vilka typer en generisk klass kan instansieras med beror på hur typerna används i klassen Exempel: Minnescell för heltal public class IntMemoryCell private Integer value = null; public void store(integer value) this.value = value; public Integer getvalue() return value; 3 4 Exempel: Minnescell för flyttal public class FloatMemoryCell private Float value = null; public void store(float value) this.value = value; Exempel: Generell (generisk) minesscell public class MemoryCell<T> private T value = null; public void store(t value) this.value = value; public Float getvalue() return value; public T getvalue() return value; 5 6
Exempel: Instansiering av generisk minesscell MemoryCell<Integer> a = new MemoryCell<Integer>(); a.store(123); Integer i = a.getvalue(); MemoryCell<String> b = new MemoryCell<String>(); b.store( Generisk ); String s = b.getvalue(); och varför inte MemoryCell<MemoryCell<String>> c = new MemoryCell<MemoryCell<String>> (); c.store(b); MemoryCell<String>x = c.getvalue(); synlighetsmodifierare static <T 1,T 2,> returtyp metodnamn parameterlista T 1,T 2, kan vara typvariabler eller typuttryck med variabler och wild-cards. Typvariablerna får användas överallt i metoden men inte som elementtyper i fält. En klassmetod får ej använda klassens generiska typparametrar utan måste sina ha egna. 7 8 Exempel Typparameter Returtyp Metodnamn public static <T> T namn() public static <T> List<T> namn() Exempel: En (?) metod som byter plats på två element i en lista class ListOps public static void swap(list<integer> l,int i,int j) Integer temp = l.get(i); l.set(i,l.get(j)); l.set(j,temp); public static void swap(list<float> l,int i,int j) Float temp = l.get(i); l.set(i,l.get(j)); l.set(j,temp); 9 10 Exempel: En metod som byter plats på två element i en lista Typradering och råa typer class ListOps public static <T> void swap(list<t> l,int i,int j) T temp = l.get(i); l.set(i,l.get(j)); l.set(j,temp); Anrop: List<String> sl = new ArrayList<>(); ListOps.swap(sl,2,37); eller ListOps.<String>swap(sl,2,37); 11 Bakåtkompatibilitet med äldre java-versioner Typvariablerna raderas av kompilatorn Kvar blir ickegeneriska råa typer Standardklasser som är generiska kan användas utan typvariabler I java betyder List ung. samma sak som List<Object> 12
Generiska typer och subtyper Är ArrayList<A> en subtyp till List<A>? Ja Får en metod med signaturen f(list<a>) anropas med ArrayList<A> som argument? Ja Om B är en subtyp till A Är då List<B> en subtyp till List<A>? Nej Får en metod med signaturen f(list<a>) anropas - med List<B> som argument? Nej - med ArrayList<B> som argument? Nej Vad är problemet? Om List<B> vore en subtyp till List<A> skulle typsystemet bli osäkert och typkorrekta program kunna ge upphov till typfel vid run-time: List<A> li = new ArrayList<B>(); // Ej tillåtet men hypotetiskt i exemplet li.add(new C()); // Statiskt typkorrekt eftersom C är subtyp till A B A När satsen exekveras får vi ett run-time-typfel eftersom li pekar på en ArrayList<B>, men C är ju inte en subtyp till B! C 13 14 Subtypsrelationer - exempel public class private int width, height; public (int width,int height) this.width = width; this.height = height; public int getarea() return width*height; public int getwidth() return width; public int getheight() return height; public boolean equals(object other) Subtypsrelationer public class extends public (int side) super(side,side); public int getside() return getwidth(); 15 16 Subtypsrelationer Subtypsrelationer public static void f1( x) public static void f2(list<> x) f2(new ArrayList<>()); //OK f1(new ()); // OK f2(new LinkedList<>()); // OK f1(new ()); // OK f2(new ArrayList<>()); // TYPFEL! f2(new LinkedList<>()); // TYPFEL! 17 18
wild card Typbegränsningsuttryck Exempel 1: Typbegränsning? matchas av vilken typ som helst <? extends T > matchas av T och alla subtyper till T public static void f3(list<? extends > x) <? super T > matchas av T och alla supertyper till T Comparator<? super T> f3(new ArrayList< >()); // OK f3(new LinkedList< >()); // OK f3(new ArrayList< >()); // OK f3(new LinkedList< >()); // OK 19 20 Exempel 2: Typbegränsning Summering av lista med valfri numerisk elementtyp public static double sumlist(list<? extends Number> numberlist) double sum = 0.0d; for ( Number n : numberlist ) sum += n.doublevalue(); return sum; Tillämpningar av begränsningsuttryck Ofta är det lämpligt att definiera likhets- och relationsoperator för basklassen i en klasshierarki, och sedan låta dessa ärvas till subklasserna. equals, compareto, compare ArrayList<Long> ll; double d = sumlist(ll); LinkedList<Float> lf; d = sumlist(lf); 21 Typbegränsningsuttryck gör detta möjligt även i kombination med generiska klasser. 22 Jämförelseklasser java.lang.comparable public interface Comparable<T> int compareto(t other); public interface Comparator<T> int compare(t lhs,t rhs); Instanser av klasser som implementerar detta gränssnitt är jämförbara med varandra Instanser av klasser som implementerar detta gränssnitt kan jämföra objekt av typ T < -1 == 0 > 1 public MyClass implements Comparable<MyClass> public int compareto(myclass x) compares MyClass objects + övr. metoder MyClass obj1,obj2; if ( obj1.compareto(obj2) > 0 ) 23 24
java.lang.comparable java.util.comparator public class Person Ordna Personobjekt i person- Implements Comparable< Person> nummerordning private String pnr; public int compareto(person other) return pnr.compareto(other.pnr); Delegera jämförelsen till strängklassen (for example). public YourClass public void yourmethod(, Comparator<some type> comp ) if ( comp.compare(x,y) == ) + övr. metoder // konstruktor och andra metoder är utelämnade 25 26 StringComparator public class StringComparator implements Comparator<String> public int compare(string s1,string s2) return s1.compareto(s2); YourClass obj; obj.yourmethod(,new StringComparator()); Storleksjämförelse av figurer public class Comparator implements Comparator<> public int compare( a, b) if ( a.getarea() < b.getarea() ) return -1; else if ( a.getarea() == b.getarea() ) return 0; else return 1; Detta är bara ett bland flera möjliga sätt att jämföra rektanglar! 27 28 Jämförelser och subtyper Comparator rcomp = new Comparator(); r1 = new (5,4); r2 = new (3,4); sq1 = new (12), sq2 = new (37); rcomp.compare(r1,r2); // 1 rcomp.compare(sq1,sq2); // -1 En generisk mängdklass (forts. från förel. 1) public interface GenericSet<T> void add( T x ); boolean contains( T x ); Tillåtet eftersom en kvadrat är en rektangel 29 30
En generisk ordnad mängdklass public class GenericOrderedSet<T> implements GenericSet<T> public GenericOrderedSet(Comparator<T> comp) public void add(t x) public boolean contains(t x) public int size() public T get(int i) Returnerar det i:te elementet i storleksordning mindre bra men vi återkommer till det! Mängder av rektanglar Comparator rcomp = new Comparator(); GenericOrderedSet<> rectset = new GenericOrderedSet<>(rcomp); rectset.add(new (5,4)); rectset.add(new (2,3)); rectset.add(new (3,4)); // störst // minst // mellan (rectset.get(2)).equals(new (5,4)) // true 31 32 Mängder av kvadrater? Comparator rcomp = new Comparator(); TYPFEL! rcomp borde ha typen Comparator<> men har typen Comparator<> GenericOrderedSet<> squareset = new GenericOrderedSet<>(rcomp); Typbegränsningsuttrycket <? super T> public class GenericOrderedSet<T> implements GenericSet<T> private static final int DEFAULT_CAPACITY = 256; private static final int SIZE_INCREMENT = 128; private int capacity = DEFAULT_CAPACITY; // Array capacity private int size = 0; // Number of distinct elements private T[] array; private Comparator<? super T> comp; // konstruktor public GenericOrderedSet(Comparator<? super T> comp) // konstruktorn GenericOrderedSet(Comparator<T> comp) 33 public void add(t x) public boolean contains(t x) public T get(int i) 34 GenericOrderedSet.contains GenericOrderedSet.add // Constructor public GenericOrderedSet(Comparator<? super T> comp) this.comp = comp; array = (T[])new Object[capacity]; public boolean contains(t x) for ( int i = 0; i < size; i++ ) if ( array[i].equals(x) ) return true; return false; 35 public void add( T x ) if ( this.contains(x) return; if ( size == capacity ) // buffer full? T[] old = array; // handle to old array // allocate a twice as big array capacity += SIZE_INCREMENT; array = (T[])new Object[capacity]; // copy elements for( int i = 0; i < size; i++ ) array[i] = old[i]; // insert the new element insert(x); 36
GenericOrderedSet.insert private void insert(t x) // Find the insertion point for x. int i = size; while ( i > 0 && comp.compare(array[i-1],x) > 0 ) array[i] = array[i-1]; i--; array[i] = x; // and then insert x size++; 37 Exemplet på bild 32 igen GenericOrderedSet<> squareset = new GenericOrderedSet<>(rcomp); // konstruktorn GenericOrderedSet(Comparator<? super T> comp) TYPKORREKT! Comparator<> är typkompatibel med Comparator<? super > eftersom <? super > matchas av (se bild 18) 38