1. Alla klasser ärverObject
Alla klasser i Java ärver implicit Object
klassen.
Vi kommer att analysera vad arv är och hur det fungerar i Java i Java Core-uppdraget. För nu ska vi överväga ett enkelt faktum som följer av detta:
Ett objekt av vilken klass som helst kan tilldelas en Object
variabel. Exempel:
Koda | Notera |
---|---|
|
Variabeln o lagrar en referens till ett Scanner objekt |
|
Variabeln o lagrar en referens till ett String objekt |
|
Variabeln o lagrar en referens till ett Integer objekt |
|
Variabeln o lagrar en referens till ett String objekt |
Det är här de goda nyheterna slutar. Kompilatorn håller inte reda på den ursprungliga typen av objekt som sparats i en Object
variabel, så du kommer inte att kunna anropa andra metoder på det sparade objektet än klassens metoder Object
.
Om du behöver anropa metoderna som är associerade med objektets ursprungliga typ, måste du först spara en referens till den i en variabel av rätt typ och sedan anropa metoderna för den variabeln:
Koda | Notera |
---|---|
|
Programmet kommer inte att kompilera. Klassen Object har ingen nextInt() metod. |
|
Detta kommer att fungera. Här sparar vi en referens till ett Scanner objekt i en Scanner variabel med hjälp av en typecast-operator . |
Du kan inte bara gå och tilldela en Object
variabel till en Scanner-variabel, även om Object
variabeln lagrar en referens till ett Scanner
objekt. Men du kan göra detta om du använder typecast-operatorn , som du redan känner till. Detta är dess allmänna utseende:
Type name1 = (Type) name2;
Var name1
är namnet på en Type
variabel, och name2
är namnet på en Object
variabel som lagrar en referens till ett Type
objekt.
Typecasting
Om variabelns typ och objektets typ inte stämmer överens, kommer a ClassCastException
att kastas. Exempel:
Koda | Notera |
---|---|
|
Ett fel kommer att uppstå vid körning: a ClassCastException kommer att kastas här |
Det finns ett sätt att undvika detta fel i Java: vi gör detta genom att kontrollera typen av objekt som lagras i en variabel :
name instanceof Type
Operatören instanceof
kontrollerar om name
variabeln är ett Type
objekt.
Som ett exempel, låt oss hitta en sträng i en rad olika objekt:
Koda | Notera |
---|---|
|
Autoboxning omvandlar dessa värden till respektive Integer , String , och Double . Slinga över arrayen av objekt Om objektet är ett String Spara det till en String variabel Visa variabeln på skärmen. |
2. Varför generika dök upp - samlingar
Låt oss återgå till samlingarna.
Så fort Java-utvecklare skapade ArrayList
klassen ville de göra den universell, så att den kunde lagra vilken typ av objekt som helst. Så de använde en array av Object
s för att lagra elementen.
Styrkan med detta tillvägagångssätt är att du kan lägga till ett objekt av vilken typ som helst i samlingen.
Naturligtvis finns det flera svagheter.
Nackdel 1.
Det var alltid nödvändigt att skriva en typkonverteringsoperator när man hämtade element från en samling:
Koda | Notera |
---|---|
|
Skapa en samling för att lagra referenser till Object objekt Fyll samlingen med siffror, 10 , 20 ... 100 ; Summa elementen i samlingen Typecasting är nödvändig |
Nackdel 2.
Det fanns ingen garanti för att en samling innehåller en specifik typ av element
Koda | Notera |
---|---|
|
Skapa en samling för att lagra referenser till Object objekt Vi fyller samlingen med siffror representerade som Double objekt: 0.0 , 2.5 , 5.0 , ... Summa elementen i samlingen Det kommer att uppstå ett fel: a Double kan inte castas till enInteger |
Data kan läggas in i samlingen var som helst:
- i en annan metod
- i ett annat program
- från en fil
- över nätverket
Nackdel 3.
Uppgifterna i samlingen kan ändras av misstag.
Du kan skicka en samling fylld med dina data till någon metod. Den metoden, skriven av en annan programmerare, lägger till sina data till din samling.
Namnet på samlingen anger inte tydligt vilka typer av data som kan lagras i den. Och även om du ger din variabel ett tydligt namn, kan en referens till den skickas till ett dussintal metoder, och de metoderna kommer definitivt inte att veta något om variabelns ursprungliga namn.
3. Generika
I Java elimineras alla dessa problem av denna coola sak som kallas generika.
I Java betyder generika möjligheten att lägga till typparametrar till typer. Resultatet är en komplex komposittyp. Den allmänna synen på en sådan sammansatt typ är denna:
ClassName<TypeParameter>
Detta är en generisk klass. Och den kan användas överallt där du normalt använder klasser.
Koda | Beskrivning |
---|---|
|
Skapa variabler |
|
Skapa objekt |
|
Skapar arrayer |
Endast Integer
variabler kan lagras i en sådan samling:
Koda | Beskrivning |
---|---|
|
ArrayList samling med Integer element Detta är tillåtet Och detta kommer också att fungera
Autoboxning
Men detta är inte tillåtet: kompileringsfel |
Du kommer att lära dig hur du skapar dina egna klasser med typparametrar i Java Collections-uppdraget. För nu ska vi titta på hur man använder dem och hur de fungerar.
4. Hur generika fungerar
Egentligen är generika fruktansvärt primitiva.
Kompilatorn ersätter helt enkelt generiska typer med vanliga typer. Men när metoder av en generisk typ används, lägger kompilatorn till en typecast-operator för att casta parametrar till typparametrarna:
Koda | Vad kompilatorn gör |
---|---|
|
|
|
|
|
|
|
|
Anta att vi har en metod som summerar talen i en samling heltal:
Koda | Vad kompilatorn gör |
---|---|
|
|
Generika är med andra ord ett slags syntaktisk socker, precis som autoboxning, men lite mer. Med autoboxning lägger kompilatorn till metoder för att konvertera en int
till en Integer
och vice versa, och för generika lägger den till typecast-operatorer.
Efter att kompilatorn har kompilerat dina generiska klasser med typparametrar, konverteras de helt enkelt till vanliga klasser och typecast-operatorer. Information om typargumenten som skickas till variabler av generiska typer går förlorad. Denna effekt kallas även typradering .
Ibland behöver programmerare som skriver generiska klasser (klasser med typparametrar) verkligen informationen om de typer som skickas som argument. I Java Collections-uppdraget får du lära dig hur du hanterar detta och vad det innebär.
5. Lite fakta om generika
Här är några mer intressanta fakta om generika.
Klasser kan ha flera typparametrar. Det ser ut ungefär så här:
ClassName<TypeParameter1, TypeParameter2, TypeParameter3>
Egentligen är detta inte riktigt förvånande. Överallt där kompilatorn kan lägga till en operatör för att casta till en typ, kan den lägga till flera typecast-operatorer.
Exempel:
Koda | Notera |
---|---|
|
Metodens put första parameter är en Integer och den andra är enString |
Generiska typer kan också användas som parametrar . Det ser ut ungefär så här:
ClassName<TypeParameter<TypeParameterParameter>>
Anta att vi vill skapa en lista som lagrar listor med strängar. I det här fallet kommer vi att få något i stil med detta:
// List of greetings
ArrayList<String> listHello = new ArrayList<String>();
listHello.add ("Hello");
listHello.add ("Hi");
// List of goodbyes
ArrayList<String> listBye = new ArrayList<String>();
listBye.add("Bye");
listBye.add ("Goodbye");
// List of lists
ArrayList<ArrayList<String>> lists = new ArrayList<ArrayList<String>>();
lists.add(listHello);
lists.add(listBye);
Generiska typer (typer med typparametrar) kan också användas som arraytyper. Det ser ut ungefär så här:
ClassName<TypeParameter>[] array = new ClassName<TypeParameter>[size];
Det är inget magiskt som händer här: vinkelparenteserna indikerar bara typnamnet:
Koda | Icke-generisk motsvarighet |
---|---|
|
|
|
|
|
|