1. Alla klasser ärverObject

Alla klasser i Java ärver implicit Objectklassen.

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 Objectvariabel. Exempel:

Koda Notera
Object o = new Scanner(System.in);
Variabeln olagrar en referens till ett Scannerobjekt
Object o = new String();
Variabeln olagrar en referens till ett Stringobjekt
Object o = new Integer(15);
Variabeln olagrar en referens till ett Integerobjekt
Object o = "Hello";
Variabeln olagrar en referens till ett Stringobjekt

Det är här de goda nyheterna slutar. Kompilatorn håller inte reda på den ursprungliga typen av objekt som sparats i en Objectvariabel, 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
Object o = new Scanner(System.in);
int x = o.nextInt();
Programmet kommer inte att kompilera. Klassen Objecthar ingen nextInt()metod.
Object o = new Scanner(System.in);

Scanner console = (Scanner) o;

int x = console.nextInt();
Detta kommer att fungera.

Här sparar vi en referens till ett Scannerobjekt i en Scannervariabel med hjälp av en typecast-operator .

Du kan inte bara gå och tilldela en Objectvariabel till en Scanner-variabel, även om Objectvariabeln lagrar en referens till ett Scannerobjekt. 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 Typevariabel, och name2är namnet på en Objectvariabel som lagrar en referens till ett Typeobjekt.

Typecasting

Om variabelns typ och objektets typ inte stämmer överens, kommer a ClassCastExceptionatt kastas. Exempel:

Koda Notera
Object o = new Integer(5);
String s = (String) o;
Ett fel kommer att uppstå vid körning:
a ClassCastExceptionkommer 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 instanceofkontrollerar om namevariabeln är ett Typeobjekt.

Som ett exempel, låt oss hitta en sträng i en rad olika objekt:

Koda Notera
Object[] objects = {10, "Hello", 3.14};

for (int i = 0; i < objects.length; i++)
{
   if (objects[i] instanceof String)
   {
      String s = (String) objects[i];
      System.out.println(s);
   }
}
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 Stringvariabel
Visa variabeln på skärmen.


2. Varför generika dök upp - samlingar

Låt oss återgå till samlingarna.

Så fort Java-utvecklare skapade ArrayListklassen ville de göra den universell, så att den kunde lagra vilken typ av objekt som helst. Så de använde en array av Objects 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
ArrayList numbers = new ArrayList();


for (int i = 0; i < 10; i++)
   numbers.add(i * 10);


int sum = 0;
for (int i = 0; i < 10; i++)
{
   sum = sum + (Integer) numbers.get(i);
}
Skapa en samling för att lagra referenser till Objectobjekt

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
ArrayList numbers = new ArrayList();


for (int i = 0; i < 10; i++)
   numbers.add(i * 2.5);


int sum = 0;
for (int i = 0; i < 10; i++)
{
   sum = sum + (Integer) numbers.get(i);
}
Skapa en samling för att lagra referenser till Objectobjekt

Vi fyller samlingen med siffror representerade som Doubleobjekt:
0.0, 2.5, 5.0, ...


Summa elementen i samlingen


Det kommer att uppstå ett fel: a Doublekan 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

Generika i Java

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
ArrayList<Integer> list;
Skapa variabler
list = new ArrayList<Integer> ();
Skapa objekt
ArrayList<Integer>[] array;
Skapar arrayer

Endast Integervariabler kan lagras i en sådan samling:

Koda Beskrivning
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(new Integer(1));
list.add(2);
list.add("Hello");
ArrayListsamling med Integerelement
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
ArrayList<Integer> list = new ArrayList<Integer>();
ArrayList list = new ArrayList();
list.add(1);
list.add( (Integer) 1 );
int x = list.get(0);
int x = (Integer) list.get(0);
list.set(0, 10);
list.set(0, (Integer) 10);

Anta att vi har en metod som summerar talen i en samling heltal:

Koda Vad kompilatorn gör
public int sum(ArrayList<Integer> numbers)
{
   int result = 0;

   for (int i = 0; i < numbers.size(); i++)
      result = result + numbers.get(i);

   return result;
}
public int sum(ArrayList numbers)
{
   int result = 0;

   for (int i = 0; i < numbers.size(); i++)
      result = result + (Integer) numbers.get(i);

   return result;
}

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 inttill en Integeroch 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
HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put(7, "Hello");
map.put(-15, "Hello");
Metodens putförsta parameter är en Integeroch 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
ArrayList<String>[] list = new ArrayList<String>[10];
StringArrayList[] list = new StringArrayList[10];
ArrayList<Integer>[] list = new ArrayList<Integer>[10];
IntegerArrayList[] list = new IntegerArrayList[10];
ArrayList<Scanner>[] list = new ArrayList<Scanner>[10];
ScannerArrayList[] list = new ScannerArrayList[10];