Kom-pa-TI-bilitet
Java: enkel fråga om Generics
Visa signatur
Visa signatur
Vill du ha svar? Citera mig gärna.
När jag tittar på tutorials för Generics skrivs en tilldelning oftast på detta vis:
List<String> myList = new ArrayList<String>();
Vad jag har förstått är generics i Java endast ett hjälpmedel inför kompilering; i den resulterande bytekoden finns inga spår av konkret typtilldelning pga. "erasure".
Det som jag inte riktigt förstått är varför man bör ange <String> vid ArrayList()-instansieringen. Variabeln myList är ju den referens som håller objektet så vad är det egentliga värdet av att skriva ... = new ArrayList<String>() istället för new ArrayList()?
Följande är, enligt NetBeans, fullt korrekt:
List<String> foo = new ArrayList<String>();
List<String> bar = new ArrayList();
foo.add("Hello");
bar.add("World");
foo.add(bar.get(0));
String out = foo.get(1);
System.out.println(out);
Kom-pa-TI-bilitet
Det som jag inte riktigt förstått är varför man bör ange <String> vid ArrayList()-instansieringen. Variabeln myList är ju den referens som håller objektet så vad är det egentliga värdet av att skriva ... = new ArrayList<String>() istället för new ArrayList()?
Jag gissar lite nu, men:
Att inte ange <String> leder ju till en varning. Alternativet skulle vara att inte varna vid följande tillfälle:
List foo = new ArrayList<Integer>();
foo.add(5);
List<String> bar = foo; // Ungefär som "bar = ArrayList()"
såvida det inte skulle vara olika typ-varningar vid instantieringar av typen Type var = new Type() jämfört med Type var = otherVar. (Och hur praktiskt detta skulle vara känner jag ej till.)
Vill du ha svar? Citera mig gärna.
Jag gissar lite nu, men:
Att inte ange <String> leder ju till en varning. Alternativet skulle vara att inte varna vid följande tillfälle:
List foo = new ArrayList<Integer>();
foo.add(5);
List<String> bar = foo; // Ungefär som "bar = ArrayList()"
såvida det inte skulle vara olika typ-varningar vid instantieringar av typen Type var = new Type() jämfört med Type var = otherVar. (Och hur praktiskt detta skulle vara känner jag ej till.)
Hm, NetBeans ger inga varningar för mig, men jag har på känn att Eclipse skulle göra det..
Vad jag menar är att all type-checking sker via myList-variabeln. I ditt exempel så är foo en lista av Object. Koden
List foo = new ArrayList<Integer>();
foo.add(42);
Integer i = foo.get(0);
ger mycket riktigt RuntimeError då man försöker tilldela Object till Integer utan cast.
Min fråga gäller det omvända exemplet: List<String> myList = ... säger klart och tydligt att Listan myList bara tar emot Strings, så var i ligger värdet att deklarera ArrayList-en som <String>? Mitt första exempel kompilerar och körs utan problem.
edit: jag läste om ditt exempel och har lite bättre koll på vad du menar. För att förtydliga vad jag menar så ponera följande:
List<String> foo = new ArrayList<String>();
foo.add("Hello");
List bar = foo;
bar.add(42);
Detta är också kompilerande, körbar kod, trots att foo har deklarerats med ArrayList<String> specifikt. Det är delen ... <String> foo = ... som är signifikant; att ArrayList är av typ String verkar inte betyda någonting.
Kom-pa-TI-bilitet
Mitt nått gammalt eclipse projekt gav varningar när jag öppnade det senare, just på detta, vet inte om det beror på java versionen man använder, men helt klart bäst att köra på nyare sättet tycker jag, dvs ange typ på alla ställen.
Min fråga gäller det omvända exemplet: List<String> myList = ... säger klart och tydligt att Listan myList bara tar emot Strings, så var i ligger värdet att deklarera ArrayList-en som <String>? Mitt första exempel kompilerar och körs utan problem.
Det är väl förmodligen helt enkelt så att javakompilatorn inte har den finessen i sig. I en tilldelning behöver ju vänsterledet vara av samma typ som (eller en basklass till) högerledet, men om man skriver
List<String> list = new ArrayList();
...så är ju detta ekvivalent med
List<String> list = new ArrayList<Object>();
Typkonflikten är ju då rätt uppenbar.
Naturligtvis hade man ju kunnat bygga kompilatorn så att den behandlar den första deklarationen som ett specialfall och implicit trixar och fixar, men det har inte gjorts.
Man kan komma runt problemet med att skriva samma sak två gånger ändå, genom att utnytta den begränsade typinferens man kan få genom statiska metoder. (Idén är inte min egen, utan snodd från den utmärkta boken Effective Java.) Säg att vi någonstans skulle ha följande:
Map<String, List<Integer>> myMap = new HashMap<String, List<Integer>>();
Om vi definierar en statisk metod newHashMap() enligt:
static <K,V> Map<K,V> newHashMap() { return new HashMap<K,V>(); }
...så kan vi ändra ovanstående deklaration till:
Map<String, List<Integer>> myMap = newHashMap();
Det gör koden lite mer lättläst. Man kan lätt skapa en massa sådana smidiga newWhatever-metoder i en liten utilityklass (säg att den får namnet com.foo.CollectionUtil) och göra importen
import static com.foo.CollectionUtil.*;
...så behöver man bara skriva newWhatever() och inte CollectionUtil.newWhatever() när man vill använda dem.
Hm, NetBeans ger inga varningar för mig
Det går att ställa in via Tools->options->editor->hints->standard javac warnings ("unchecked" och "raw types").
Copyright © 1999–2023 Geeks AB. Allt innehåll tillhör Geeks AB.
Citering är tillåten om källan anges.