1. Alle klasser arverObject

Alle klasser i Java arver implisitt Objectklassen.

Vi vil analysere hva arv er og hvordan det fungerer i Java i Java Core-oppdraget. For nå skal vi vurdere ett enkelt faktum som følger av dette:

Et objekt av en hvilken som helst klasse kan tilordnes til en Objectvariabel. Eksempel:

Kode Merk
Object o = new Scanner(System.in);
Variabelen olagrer en referanse til et Scannerobjekt
Object o = new String();
Variabelen olagrer en referanse til et Stringobjekt
Object o = new Integer(15);
Variabelen olagrer en referanse til et Integerobjekt
Object o = "Hello";
Variabelen olagrer en referanse til et Stringobjekt

Det er her de gode nyhetene slutter. Kompilatoren holder ikke styr på den opprinnelige typen objekt som er lagret i en Objectvariabel, så du vil ikke kunne kalle andre metoder på det lagrede objektet enn metodene til Objectklassen.

Hvis du trenger å kalle metodene knyttet til objektets opprinnelige type, må du først lagre en referanse til den i en variabel av riktig type, og deretter kalle metodene på den variabelen:

Kode Merk
Object o = new Scanner(System.in);
int x = o.nextInt();
Programmet vil ikke kompilere. Klassen Objecthar ingen nextInt()metode.
Object o = new Scanner(System.in);

Scanner console = (Scanner) o;

int x = console.nextInt();
Dette vil fungere.

Her lagrer vi en referanse til et Scannerobjekt i en Scannervariabel ved hjelp av en typecast-operator .

Du kan ikke bare gå og tilordne en Objectvariabel til en Scanner-variabel, selv om Objectvariabelen lagrer en referanse til et Scannerobjekt. Men du kan gjøre dette hvis du bruker typecast-operatoren , som du allerede vet om. Dette er dens generelle utseende:

Type name1 = (Type) name2;

Hvor name1er navnet på en Typevariabel, og name2er navnet på en Objectvariabel som lagrer en referanse til et Typeobjekt.

Typecasting

Hvis variabelens type og objektets type ikke samsvarer, vil a ClassCastExceptionbli kastet. Eksempel:

Kode Merk
Object o = new Integer(5);
String s = (String) o;
En feil vil oppstå under kjøring:
a ClassCastExceptionvil bli kastet her

Det er en måte å unngå denne feilen i Java: vi gjør dette ved å sjekke typen objekt som er lagret i en variabel :

name instanceof Type

Operatøren instanceofsjekker om namevariabelen er et Typeobjekt.

Som et eksempel, la oss finne en streng i en rekke forskjellige objekter:

Kode Merk
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);
   }
}
Autoboksing vil konvertere disse verdiene til henholdsvis Integer, String, og Double.

Sløyfe over utvalget av objekter

Hvis objektet er en String

Lagre det i en Stringvariabel
Vis variabelen på skjermen.


2. Hvorfor generika dukket opp - samlinger

La oss gå tilbake til samlingene.

Så snart Java-utviklere opprettet ArrayListklassen, ønsket de å gjøre den universell, slik at den kunne lagre alle typer objekter. Så de brukte en rekke Objects for å lagre elementene.

Styrken med denne tilnærmingen er at du kan legge til et objekt av enhver type i samlingen.

Selvfølgelig er det flere svakheter.

Ulempe 1.

Det var alltid nødvendig å skrive en typekonverteringsoperator når du henter elementer fra en samling:

Kode Merk
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);
}
Opprett en samling for å lagre referanser til Objectobjekter

Fyll samlingen med tall 10, 20, ... 100;



Sum elementene i samlingen


Typecasting er nødvendig

Ulempe 2.

Det var ingen garanti for at en samling inneholder en bestemt type element

Kode Merk
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);
}
Opprett en samling for å lagre referanser til Objectobjekter

Vi fyller samlingen med tall representert som Doubleobjekter:
0.0, 2.5, 5.0, ...


Sum elementene i samlingen


Det vil oppstå en feil: a Doublekan ikke kastes til enInteger

Data kan legges inn i samlingen hvor som helst:

  • i en annen metode
  • i et annet program
  • fra en fil
  • over nettverket

Ulempe 3.

Dataene i samlingen kan endres ved et uhell.

Du kan overføre en samling fylt med dataene dine til en eller annen metode. Denne metoden, skrevet av en annen programmerer, legger dataene til samlingen din.

Navnet på samlingen indikerer ikke tydelig hvilke typer data som kan lagres i den. Og selv om du gir variabelen din et klart navn, kan en referanse til den overføres til et dusin metoder, og disse metodene vil definitivt ikke vite noe om det opprinnelige navnet på variabelen.


3. Generiske legemidler

Generikk i Java

I Java elimineres alle disse problemene av denne kule tingen som kalles generika.

I Java betyr generikk muligheten til å legge til typeparametere til typer. Resultatet er en kompleks kompositttype. Det generelle synet på en slik sammensatt type er dette:

ClassName<TypeParameter>

Dette er en generisk klasse. Og den kan brukes uansett hvor du vanligvis bruker klasser.

Kode Beskrivelse
ArrayList<Integer> list;
Opprette variabler
list = new ArrayList<Integer> ();
Opprette objekter
ArrayList<Integer>[] array;
Opprette arrays

Bare Integervariabler kan lagres i en slik samling:

Kode Beskrivelse
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(new Integer(1));
list.add(2);
list.add("Hello");
ArrayListsamling med Integerelementer
Dette er tillatt
Og dette vil også fungere
Autoboksing

Men dette er ikke tillatt: kompileringsfeil

Du vil lære hvordan du lager dine egne klasser med typeparametere i Java Collections-oppdraget. Foreløpig skal vi se på hvordan du bruker dem og hvordan de fungerer.


4. Hvordan generika fungerer

Egentlig er generika fryktelig primitive.

Kompilatoren erstatter ganske enkelt generiske typer med vanlige typer. Men når metoder av en generisk type brukes, legger kompilatoren til en typecast-operatør for å kaste parametere til typeparameterne:

Kode Hva kompilatoren gjø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 at vi har en metode som summerer tallene i en samling av heltall:

Kode Hva kompilatoren gjø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 er med andre ord en slags syntaktisk sukker, akkurat som autoboksing, men litt mer. Med autoboksing legger kompilatoren til metoder for å konvertere en inttil en Integerog omvendt, og for generiske stoffer legger den til typecast-operatører.

Etter at kompilatoren har kompilert de generiske klassene dine med typeparametere, blir de ganske enkelt konvertert til vanlige klasser og typecast-operatører. Informasjon om typeargumentene som sendes til variabler av generiske typer går tapt. Denne effekten kalles også type sletting .

Noen ganger trenger programmerere som skriver generiske klasser (klasser med typeparametere) virkelig informasjonen om typene som sendes som argumenter. I Java Collections-oppdraget lærer du hvordan du håndterer dette og hva det innebærer.



5. Noen få fakta om generiske legemidler

Her er noen flere interessante fakta om generika.

Klasser kan ha flere typeparametere. Det ser omtrent slik ut:

ClassName<TypeParameter1, TypeParameter2, TypeParameter3>

Egentlig er dette egentlig ikke overraskende. Hvor som helst hvor kompilatoren kan legge til en operatør for å kaste til én type, kan den legge til flere typecast-operatører.

Eksempler:

Kode Merk
HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put(7, "Hello");
map.put(-15, "Hello");
Metodens putførste parameter er en Integer, og den andre er enString

Generiske typer kan også brukes som parametere . Det ser omtrent slik ut:

ClassName<TypeParameter<TypeParameterParameter>>

Anta at vi ønsker å lage en liste som vil lagre lister med strenger. I dette tilfellet får vi noe slikt:

// 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);

Generiske typer (typer med typeparametere) kan også brukes som matrisetyper. Det ser omtrent slik ut:

ClassName<TypeParameter>[] array = new ClassName<TypeParameter>[size];

Det er ikke noe magisk som skjer her: vinkelparentesene indikerer bare typenavnet:

Kode Ikke-generisk motstykke
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];