Java 5.0: Säkrare, snyggare, smidigare Sven-Olof Nyström Uppsala Universitet 7 april 2005 Generiska typer Exempel: List<Integer>l = new ArrayList><Integer>(); l.add(new Integer(42)); (Mer om detta senare.) 1 3 Viktigaste utökningarna av språket: Generiska typer Autoboxing/auto-unboxing Utökade for-loopar Uppräkningar/enumerations Vararg Format Statisk import Autoboxing/unboxing, bakgrund För var och en av de åtta primitiva datatyperna finns en motsvarande klass, tex int Integer boolean Boolean Man kan konvertera mellan genom att skriva Integer x = new Integer(42); int y = x.intvalue(); 2 4
Autoboxing/unboxing i java 5.0 I Java 5.0 sker konverteringen automatiskt, man kan skriva Integer x = 42; int y = x; Man kan klara sig utan de primitiva typerna: Integer s = 0; for (Integer i = 0; i<10; i++) { s = s + i; For-loopar Regel: Det vi itererar över måste vara en array eller definiera gränssnittet Iteratable Man kan alltså definiera egna itererbara datatyper! Antagligen ej att rekommendera! (Varför inte?) 5 7 Exempel List<Integer> =... for(integer i : l) {... eller for(int i : l) {... int [] a = {4,7,1,7,6,9,1; for (int i : a) { System.out.println (i); Utökad for-loop Uppräkningar (enumerations) Anta att vi vill definiera en datatyp som kan anta tre olika värden, tex RED, BLUE, WHITE En lösning: public class Color { public static final int RED = 1; public static final int BLUE = 2; public static final int WHITE = 3; int mycolor = Color.RED; Ingen typsäkerhet! 6 8
Alternativ: public class Color { private int color; private Color (int c) {color = c; public static final Color RED = new Color(1); public static final Color BLUE = new Color(2); public static final Color WHITE = new Color(3); Color mycolor = Color.RED; Vararg Man kan definiera metoder med variabelt antal argument static void atest (Object... args) { for (int i = 0; i < args.length; i++) { System.out.println(args[i]); atest("en ", "sorglig", "historia"); atest(43, 3.13, false); 9 11 Uppräkningar i Java 5.0 Exempel: public enum Color {red, blue, white; definierar en uppräkning. (Liknar en klassdefinition, men kan ej ärvas.) Color.values() ger en samling av alla värden. for (Color i : Color.values()) { System.out.println (i); Format Inte så spännade, men bekvämt! int x = 42; double y = 3.14; String s = "Foo"; System.out.printf("Result: %d -> %f %n%n %s %n", x, y, s); 10 12
Statisk import Import av statiska attribut (klassvariabler och klassmetoder). Java 1.4.2: public class Old { public static void main(string[] arg) { double x = Double.parseDouble(arg[0]); double y = Math.sin(x); System.out.println("sin("+x+") = " + y); class Box<X> { X x; X get() { return x; void put (X x0) { x = x0; Generiska typer exempel 13 15 Statisk import, Java 5.0: import static java.lang.double.*; import static java.lang.system.*; import static java.lang.math.*; public class Stat { public static void main(string[] arg) { double x = parsedouble(arg[0]); double y = sin(x); out.println("sin("+x+") = " + y); Genererisk typ test public static void main (String [] arg) { Box<String> b = new Box<String> (); b.put("foo"); String s = b.get(); System.out.println(s); Box<Integer> c = new Box<Integer> (); c.put(42); int i = c.get(); System.out.println(i); 14 16
Allmänt om generiska typer Parametrarna lever bara vid kompileringstillfället. Den bytekod som genereras hade lika gärna kunnat genereras av vanlig javakod (Java 1.4.2). Jfr templates i C++. Typ-parametrarna används när kompilatorn kontrollerar programmet och sätter in vissa konverteringar (autobox/unboxing). Frekvens, utskrift System.out.println(m.size()+ " distinct words detected:"); for(map.entry e : m.entryset()) { System.out.println(e.getKey() + " : " + e.getvalue()); 17 19 Frekvens public static void main(string args[]) { Map <String,Integer> m = new HashMap<String, Integer>(); for (String a : args) { if (m.containskey(a)) { int freq = m.get(a); m.put(a, freq+1); else { m.put(a, 1); Kort mellanspel: Klassen StringBuffer Exempel: x = "a" + 4 + "c" kan även skrivas x = new StringBuffer().append("a").append(4).append("c").toString() Den senare satsen skapar en ny (tom) stringbuffer, tar strängrepresentationen av varje operand i tur och ordning och konkatenerar till buffern. Vinst: Vi undviker att allokera ett antal temporära strängar. 18 20
Operationer på StringBuffer Ungefär som String, men tillåter uppdatering StringBuffer(String s) StringBuffer(int l) en buffer av längd l, som från början är tom StringBuffer append (boolean b) (Samma för alla andra primitiva datatyper) StringBuffer: Exempel Anta att z en strängbuffer med innehåll start". efter metodanropet z.append(le") innehåller z strängen startle". Om vi i stället gör metodanropet z.insert(4, le") kommer z att innehålla starlet". Varje strängbuffer har en viss kapacitet. Om kapaciteten överskrids, allokeras en större intern array. 21 23 Perm2.java StringBuffer Operationer (forts) StringBuffer insert (int i, boolean b) skjuter in stängen vid position i char charat(int i) void setcharat(int i, char c) Viktigaste operationer: append och insert (tar alla typer) Problem: Givet en fil med ord, hitta ord som är varandras permutationer. Lösning: Definiera en funktion alphabetize som tar en sträng och returnerar en sträng med bokstäverna i alfabetisk ordning. Bygg en tabell Map<String,List<String>> där strängarna läggs in enligt den alfabetiserade nyckeln. Två strängar med samma nyckel är varandras permutationer och hamnar på samma lista. 22 24
Alphabetize private static String alphabetize(string s) { SortedMap<Character, Integer> count = new TreeMap<Character, Integer> (); int len = s.length(); for (int i=0; i<len; i++) { char c = s.charat(i); if (count.containskey(c)) { count.put(c, count.get(c)+1); else { count.put(c, 1); Perm2, huvudprogrammet public class Perm2 { public static void main(string[] args) { int mingroupsize = Integer.parseInt(args[1]); Map<String,List<String>> m = new HashMap<String, List<String>>(); < Läs in strängarna i m > for (List<String> l : m.values()) { if (l.size() >= mingroupsize) System.out.println(l.size() + ": " + l); 25 27 Alphabetize (forts) StringBuffer result = new StringBuffer(len); for (Map.Entry<Character, Integer> e : count.entryset()) { char c = e.getkey(); int n = e.getvalue(); Perm2, läs in strängarna i m Map<String,List<String>> m = new HashMap<String, List<String>>(); try { < Läs in strängarna, loop > for (int i=0; i<n; i++) result.append(c); return result.tostring(); catch(ioexception e) { System.err.println(e); System.exit(1); 26 28
Perm2, läs in strängarna, loop BufferedReader in = new BufferedReader(new FileReader(args[0])); String word; while((word = in.readline())!= null) { String alpha = alphabetize(word); List<String> l = m.get(alpha); if (l==null) m.put(alpha, l=new ArrayList<String>()); l.add(word); Hur många typer kan ett program definiera? class Box<X> { X x; Box(X x) { this.x = x; public String tostring () { return "Box<"+x+">"; 29 31 Hur många... Lite mer om generiska typer Parametriska metoder: class Id { public static <T> T id(t x) { return x; public class Many { static <X> Box<Box<X>> boxinbox (Box<X> x) { return new Box<Box<X>>(x); public static void main (String[] args){ int n = Integer.parseInt(args[0]); Object x = 99; for (int i = 0; i<n; i++) { x = new Box<Object>(x); System.out.println(x); 30 32
Many, körexempel svenolof@harpo$ java5c Many.java svenolof@harpo$ java5 Many Exception in thread "main" java.lang.arrayindexoutofboundsexcept at Many.main(Many.java:19) svenolof@harpo$ java5 Many 5 Box<99> Box<Box<99>> Box<Box<Box<99>>> Box<Box<Box<Box<99>>>> Box<Box<Box<Box<Box<99>>>>> svenolof@harpo$ Är följande OK? Subtyper List<String> ls = new ArrayList<String>(); List<Object> lo = ls;??? Ja, för en lista av strängar är en lista av objekt??? Nej: Vid tilldelning av samligar kan information gå åt båda hållen. Exempel:lo.add(42); För att tillåta liknande typer med olika typargument kan man använda wildcards 33 35 Allmänt om generiska typer i Java Kom ihåg: typparametrarna existerar bara vid kompileringstillfället. Internt är en generisk typ inte så olik en konventionell klassdefinition. (Jfr templates i C++) Typparametrarna används för kontroll och för autoboxing/unboxing. import java.util.*; Wildcards, exempel class Wild { static void printcollection (Collection <?> c) { for (Object e:c) { System.out.println(e); 34 36
Wildcards (forts) public static void main (String[] args) { List<String> ls = new ArrayList<String>(); for (String a:args) ls.add(a); printcollection (ls); List<Integer> li = new ArrayList<Integer>(); for (int i:new int[]{7,6,9,1) li.add(i); printcollection(li); Nåt som ej kan lösas utan wildcards class Delay { Collection<?> c1; void printcollection (Collection <?> c) { if (c1!= null) for (Object e:c1) { System.out.println(e); c1 = c; 37 39 printcollection utan wildcards static <T> void printcollection (Collection <T> c) { for (Object e:c) { System.out.println(e); Klassen Delay, att notera Wildcards används på två ställen; i parametern till printcollection i typen hos instansvariabeln c1. Konvertering från mer specifik typ är OK men ej att skriva c1.add("foo"); Kan man implementera Delay i Java 1.4? 38 40
Begränsade typparametrar och wildcards Exempel: En fordonshierarki med klasser Fordon, Bil, Cykel etc. Den abstrakta klassen Fordon definierar en metod gettoppfart(). Hur definiera en metod som tar en samling av fordon och returnerar den totala toppfarten? int totaltoppfart (Collection <Fordon> c) { OK, om argumentet har typ Collection<Fordon> eller (tex) ArrayList<Fordon>. Ej OK om argumentet har typ Collection<Cykel> 41 Begränsat... Om man vill tillåta det senare argumentet... Fungerar int <T> totaltoppfart (Collection <T> c) { eller int totaltoppfart (Collection <?> c) { (Nej, varför inte?) Istället, försök med int totaltoppfart (Collection <? extends Fordon> c) { eller <T extends Fordon> int totaltoppfart (Collection <T> c) { 42