Java Generics Interview Spørgsmål (+ svar)

Denne artikel er en del af en serie: • Interviewsspørgsmål om Java Collections

• Spørgsmål om Java Type System Interview

• Java-spørgsmål om samtidige samtaler (+ svar)

• Interviewspørgsmål om Java-klassestruktur og initialisering

• Java 8 interviewspørgsmål (+ svar)

• Hukommelsesstyring i Java Interview-spørgsmål (+ svar)

• Spørgsmål til Java Generics-interview (+ svar) (nuværende artikel) • Interviewspørgsmål til Java Flow Control (+ svar)

• Spørgsmål om Java-undtagelser (+ svar)

• Spørgsmål om Java-annotationer (+ svar)

• Top forårssamarbejdsspørgsmål

1. Introduktion

I denne artikel gennemgår vi nogle eksempler på spørgsmål og svar på Java generics-interview.

Generics er et kernekoncept i Java, der først blev introduceret i Java 5. På grund af dette vil næsten alle Java-kodebaser bruge dem, hvilket næsten garanterer, at en udvikler støder på dem på et eller andet tidspunkt. Det er derfor, det er vigtigt at forstå dem korrekt, og det er derfor, de sandsynligvis bliver spurgt om under en interviewproces.

2. Spørgsmål

Q1. Hvad er en generisk typeparameter?

Type er navnet på en klasse eller interface. Som antydet af navnet, en generisk typeparameter er, når en type kan bruges som en parameter i en klasse-, metode- eller grænsefladedeklaration.

Lad os starte med et simpelt eksempel, et uden generiske stoffer, for at demonstrere dette:

offentlig grænseflade Forbruger {offentligt ugyldigt forbrug (strengparameter)}

I dette tilfælde er metodeparametertypen for forbruge () metode er Snor. Den er ikke parametreret og ikke konfigurerbar.

Lad os nu erstatte vores Snor skriv med en generisk type, som vi kalder T. Det er navngivet sådan ved konvention:

offentlig grænseflade Forbruger {offentligt ugyldigt forbrug (T-parameter)}

Når vi implementerer vores forbruger, kan vi levere den type at vi vil have det til at forbruge som et argument. Dette er en generisk type parameter:

offentlig klasse IntegerConsumer implementerer forbruger {public void consume (Integer parameter)}

I dette tilfælde kan vi nu forbruge heltal. Vi kan bytte dette ud type for hvad vi har brug for.

Q2. Hvad er nogle fordele ved at bruge generiske typer?

En fordel ved at bruge generiske stoffer er at undgå afstøbning og give typesikkerhed. Dette er især nyttigt, når du arbejder med samlinger. Lad os demonstrere dette:

Liste liste = ny ArrayList (); list.add ("foo"); Objekt o = liste.get (0); String foo = (String) o;

I vores eksempel er elementtypen på vores liste ukendt for compileren. Dette betyder, at det eneste, der kan garanteres, er, at det er et objekt. Så når vi henter vores element, et Objekt er, hvad vi får tilbage. Som forfattere af koden ved vi, at det er et Snor, men vi skal kaste vores objekt til en for at løse problemet eksplicit. Dette producerer en masse støj og kedelplade.

Dernæst, hvis vi begynder at tænke over plads til manuel fejl, bliver castingproblemet værre. Hvad hvis vi ved et uheld havde en Heltal på vores liste?

list.add (1) Objekt o = list.get (0); String foo = (String) o;

I dette tilfælde får vi en ClassCastException ved kørsel som en Heltal kan ikke kastes til Snor.

Lad os nu gentage os selv, denne gang ved hjælp af generiske lægemidler:

Liste liste = ny ArrayList (); list.add ("foo"); String o = list.get (0); // Ingen rollebesætning Heltal foo = list.get (0); // Kompileringsfejl

Som vi kan se, ved hjælp af generiske stoffer har vi en kompileringstypekontrol, som forhindrer ClassCastExceptions og fjerner behovet for støbning.

Den anden fordel er at undgå duplikering af kode. Uden generiske produkter er vi nødt til at kopiere og indsætte den samme kode, men til forskellige typer. Med generiske lægemidler behøver vi ikke gøre dette. Vi kan endda implementere algoritmer, der gælder for generiske typer.

Q3. Hvad er type sletning?

Det er vigtigt at indse, at generisk typeinformation kun er tilgængelig for compileren, ikke JVM. Med andre ord, type sletning betyder, at generisk typeinformation ikke er tilgængelig for JVM'en under kørsel, kun kompileringstid.

Begrundelsen bag det store implementeringsvalg er enkel - at bevare bagudkompatibilitet med ældre versioner af Java. Når en generisk kode kompileres til bytekode, vil det være som om den generiske type aldrig har eksisteret. Dette betyder, at samlingen vil:

  1. Udskift generiske typer med objekter
  2. Erstat afgrænsede typer (Mere om disse i et senere spørgsmål) med den første bundne klasse
  3. Indsæt ækvivalenten med rollebesætninger, når generiske objekter hentes.

Det er vigtigt at forstå type sletning. Ellers kan en udvikler blive forvirret og tro, at de er i stand til at få typen ved kørselstid:

offentlig foo (forbrugerforbruger) {Type type = forbruger.getGenericTypeParameter ()}

Ovenstående eksempel er en pseudokode svarende til, hvordan ting kan se ud uden sletning af typen, men desværre er det umuligt. Endnu engang, oplysningerne om generisk type er ikke tilgængelige under kørsel.

Q4. Hvis en generisk type udelades, når der oprettes et objekt, vil koden stadig kompileres?

Da generics ikke eksisterede før Java 5, er det muligt ikke at bruge dem overhovedet. For eksempel blev generika eftermonteret i de fleste af de standard Java-klasser såsom samlinger. Hvis vi ser på vores liste fra spørgsmål et, så ser vi, at vi allerede har et eksempel på at udelade den generiske type:

Liste liste = ny ArrayList ();

På trods af at være i stand til at kompilere, er det stadig sandsynligt, at der vil være en advarsel fra compileren. Dette skyldes, at vi mister den ekstra kompileringstidskontrol, som vi får ved at bruge generiske stoffer.

Pointen at huske er, at mens bagudkompatibilitet og sletning af typer gør det muligt at udelade generiske typer, er det dårlig praksis.

Q5. Hvordan adskiller en generisk metode sig fra en generisk type?

En generisk metode er, hvor en typeparameter introduceres til en metode,lever inden for rammerne af denne metode. Lad os prøve dette med et eksempel:

offentlig statisk T returnType (T argument) {return argument; }

Vi har brugt en statisk metode, men kunne også have brugt en ikke-statisk, hvis vi ville. Ved at udnytte typeinferens (dækket af det næste spørgsmål) kan vi påberåbe sig dette som enhver almindelig metode uden at skulle specificere nogen type argumenter, når vi gør det.

Q6. Hvad er typeforståelse?

Typeinferens er, når compileren kan se på typen af ​​et metodargument for at udlede en generisk type. For eksempel hvis vi passerede ind T til en metode, der vender tilbage T, så kan compileren finde ud af returtypen. Lad os prøve dette ved at påkalde vores generiske metode fra det foregående spørgsmål:

Integer inferredInteger = returnType (1); String inferredString = returnType ("String");

Som vi kan se, er der ikke behov for en rollebesætning, og der er ikke behov for at videregive nogen generisk type argument. Argumenttypen udleder kun returtypen.

Q7. Hvad er en afgrænset typeparameter?

Indtil videre har alle vores spørgsmål dækket generiske typerargumenter, der er ubegrænsede. Dette betyder, at vores generiske argumentargumenter kan være den type, vi ønsker.

Når vi bruger afgrænsede parametre, begrænser vi de typer, der kan bruges som generiske typeargumenter.

Lad os som et eksempel sige, at vi altid vil tvinge vores generiske type til at være en underklasse af dyr:

offentlig abstrakt klasse Cage {abstrakt ugyldigt addAnimal (T dyr)}

Ved at bruge extends, vi tvinger T at være en underklasse af dyr. Vi kunne så have et bur med katte:

Bur katBur;

Men vi kunne ikke have et bur med objekter, da et objekt ikke er en underklasse af et dyr:

Bur objektBur; // Kompileringsfejl

En fordel ved dette er, at alle dyremetoderne er tilgængelige for kompilatoren. Vi ved, at vores type udvider den, så vi kunne skrive en generisk algoritme, der fungerer på ethvert dyr. Dette betyder, at vi ikke behøver at reproducere vores metode til forskellige dyreunderklasser:

offentlig ugyldig firstAnimalJump () {T animal = animals.get (0); animal.jump (); }

Q8. Er det muligt at erklære en parameter med flere afgrænsede typer?

Det er muligt at erklære flere grænser for vores generiske typer. I vores tidligere eksempel specificerede vi en enkelt grænse, men vi kunne også angive mere, hvis vi ønsker det:

offentlig abstrakt klasse Cage

I vores eksempel er dyret en klasse, og sammenligneligt er en grænseflade. Nu skal vores type respektere begge disse øvre grænser. Hvis vores type var en underklasse af dyr, men ikke implementerede sammenlignelige, ville koden ikke kompilere. Det er også værd at huske, at hvis en af ​​de øvre grænser er en klasse, skal det være det første argument.

Q9. Hvad er en jokertegnetype?

En wildcard-type repræsenterer en ukendt type. Det detoneres med et spørgsmålstegn som følger:

offentligt statisk ugyldigt forbrugListOfWildcardType (liste liste)

Her specificerer vi en liste, der kan være af enhver type. Vi kunne sende en liste over alt til denne metode.

Q10. Hvad er et øvre afgrænset jokertegn?

Et wildcard med øvre grænser er, når en wildcard-type arver fra en betontype. Dette er især nyttigt, når du arbejder med samlinger og arv.

Lad os prøve at demonstrere dette med en gårdklasse, der opbevarer dyr, først uden jokertegnetypen:

offentlig klasse Farm {private Liste dyr; public void addAnimals (Collection newAnimals) {animals.addAll (newAnimals); }}

Hvis vi havde flere underklasser af dyr, såsom kat og hund, vi antager muligvis den forkerte antagelse om, at vi kan føje dem alle til vores gård:

farm.addDyr (katte); // Kompileringsfejl farm.addAnimals (hunde); // Kompileringsfejl

Dette skyldes, at kompilatoren forventer en samling af betontypedyret, ikke en det underklasser.

Lad os nu introducere et øvre afgrænset jokertegn til vores tilføjelsesdyrsmetode:

public void addAnimals (Collection newAnimals)

Hvis vi nu prøver igen, kompileres vores kode. Dette skyldes, at vi nu beder kompilatoren om at acceptere en samling af enhver undertype af dyr.

Q11. Hvad er et ubegrænset jokertegn?

Et ubegrænset jokertegn er et jokertegn uden øvre eller nedre grænse, der kan repræsentere enhver type.

Det er også vigtigt at vide, at wildcard-typen ikke er synonymt med objekt. Dette skyldes, at et jokertegn kan være af enhver type, mens en objekttype specifikt er et objekt (og ikke kan være en underklasse af et objekt). Lad os demonstrere dette med et eksempel:

Liste wildcardList = ny ArrayList (); Liste objectList = ny ArrayList (); // Kompileringsfejl

Igen er årsagen til, at anden linje ikke kompileres, at der kræves en liste over objekter, ikke en liste over strenge. Den første linje kompileres, fordi en liste af enhver ukendt type er acceptabel.

Q12. Hvad er et wildcard med lavere grænse?

Et wildcard med lavere grænser er, når vi i stedet for at give en øvre grænse, giver vi en nedre grænse ved hjælp af super nøgleord. Med andre ord, et lavere afgrænset jokertegn betyder, at vi tvinger typen til at være en superklasse af vores afgrænsede type. Lad os prøve dette med et eksempel:

offentlig statisk ugyldig addDogs (List list) {list.add (new Dog ("tom"))}

Ved hjælp af super, vi kunne kalde addDogs på en liste over objekter:

ArrayList objekter = ny ArrayList (); addDogs (objekter);

Dette giver mening, da et objekt er en superklasse af dyr. Hvis vi ikke brugte det jævne kort med lavere grænser, ville koden ikke kompileres, da en liste over objekter ikke er en liste over dyr.

Hvis vi tænker over det, ville vi ikke være i stand til at tilføje en hund til en liste over nogen underklasse af dyr, såsom katte eller endda hunde. Kun en superklasse af dyr. For eksempel vil dette ikke kompilere:

ArrayList objekter = ny ArrayList (); addDogs (objekter);

Q13. Hvornår vil du vælge at bruge en lavere grænse versus en øvre grænse?

Når man beskæftiger sig med samlinger, er PECS en almindelig regel til valg mellem wildcards med øvre eller nedre grænse. PECS står for producent udvider, forbruger super.

Dette kan let demonstreres ved brug af nogle standard Java-grænseflader og klasser.

Producent strækker sig betyder bare, at hvis du opretter en producent af en generisk type, skal du bruge strækker sig nøgleord. Lad os prøve at anvende dette princip på en samling for at se, hvorfor det giver mening:

offentlig statisk ugyldighed makeLotsOfNoise (liste dyr) {animals.forEach (Animal :: makeNoise); }

Her vil vi ringe lav larm() på hvert dyr i vores samling. Dette betyder, at vores samling er en producent, som alt, hvad vi gør med det, er at få det til at returnere dyr, som vi kan udføre vores operation på. Hvis vi slipper af strækker sig, ville vi ikke være i stand til at videregive lister over katte, hunde eller andre underklasser af dyr. Ved at anvende princippet om producent-udvidelser har vi størst mulig fleksibilitet.

Forbruger super betyder det modsatte af producent udvider. Alt, hvad det betyder, er at hvis vi har at gøre med noget, der forbruger elementer, så skal vi bruge super nøgleord. Vi kan demonstrere dette ved at gentage vores tidligere eksempel:

offentlig statisk ugyldig addCats (liste dyr) {animals.add (ny kat ()); }

Vi føjer kun til vores liste over dyr, så vores liste over dyr er en forbruger. Dette er grunden til, at vi bruger super nøgleord. Det betyder, at vi kunne passere i en liste over enhver superklasse af dyr, men ikke en underklasse. For eksempel, hvis vi forsøgte at videregive en liste over hunde eller katte, ville koden ikke kompilere.

Den sidste ting at overveje er, hvad man skal gøre, hvis en samling både er forbruger og producent. Et eksempel på dette kan være en samling, hvor elementer både tilføjes og fjernes. I dette tilfælde skal der bruges et ubegrænset jokertegn.

Q14. Er der nogen situationer, hvor generisk typeinformation er tilgængelig under kørsel?

Der er en situation, hvor en generisk type er tilgængelig under kørsel. Dette er, når en generisk type er en del af klassesignaturen som sådan:

offentlig klasse CatCage implementerer Cage

Ved at bruge refleksion får vi denne type parameter:

(Class) ((ParameterizedType) getClass () .getGenericSuperclass ()). GetActualTypeArguments () [0];

Denne kode er noget skør. For eksempel afhænger det af, at typeparameteren defineres i den umiddelbare superklasse. Men det viser, at JVM har denne type information.

Næste » Java Flow Control Interview Spørgsmål (+ svar) « Tidligere hukommelsesstyring i Java Interview-spørgsmål (+ svar)

$config[zx-auto] not found$config[zx-overlay] not found