Java 5 brakte generikk til Java-språket. I denne artikkelen introduserer jeg deg for generiske legemidler og diskuterer generiske typer, generiske metoder, generiske stoffer og type inferens, generiske kontroverser, og generiske og haugeforurensning.
last ned Få koden Last ned kildekoden for eksempler i denne Java 101-opplæringen. Skapt av Jeff Friesen for JavaWorld.Hva er generiske legemidler?
Generiske er en samling av relaterte språkfunksjoner som gjør det mulig for typer eller metoder å operere på objekter av forskjellige typer, samtidig som de gir kompileringssikkerhet. Generiske funksjoner løser problemet med java.lang.ClassCastException
blir kastet ved kjøretid, som er et resultat av kode som ikke er typesikker (dvs. å kaste gjenstander fra deres nåværende typer til inkompatible typer).
Generics og Java Collections Framework
Generiske stoffer er mye brukt i Java Collections Framework (formelt introdusert i fremtiden Java 101 artikler), men de er ikke eksklusive for det. Generiske er også brukt i andre deler av Java standard klassebibliotek inkludert java.lang.Klasse
, java.lang. sammenlignbar
, java.lang.ThreadLocal
, og java.lang.ref.WeakReference
.
Tenk på følgende kodefragment, som demonstrerer mangelen på typesikkerhet (i sammenheng med Java Collections Framework’s java.util.LinkedList
klasse) som var vanlig i Java-kode før generikk ble introdusert:
Liste doubleList = ny LinkedList (); doubleList.add (ny Double (3.5)); Dobbelt d = (Dobbelt) doubleList.iterator (). Neste ();
Selv om målet med programmet ovenfor er å lagre bare java.lang. dobbelt
objekter i listen, hindrer ingenting andre typer gjenstander fra å bli lagret. For eksempel kan du spesifisere doubleList.add ("Hei");
for å legge til en java.lang.Streng
gjenstand. Men når du lagrer en annen type gjenstand, den endelige linjen (Dobbelt)
cast operatør årsaker ClassCastException
å bli kastet når de konfronteres med en ikke-Dobbelt
gjenstand.
Fordi denne mangelen på typesikkerhet ikke blir oppdaget før kjøretid, kan en utvikler kanskje ikke være klar over problemet, og overlate det til klienten (i stedet for kompilatoren) å oppdage. Generics hjelper kompilatoren med å varsle utvikleren om problemet med å lagre et objekt med et ikke-Dobbelt
skriv inn listen ved å tillate utvikleren å merke listen som bare inneholder Dobbelt
gjenstander. Denne hjelpen er vist nedenfor:
Liste doubleList = ny LinkedList (); doubleList.add (ny Double (3.5)); Dobbelt d = doubleList.iterator (). Neste ();
Liste
lyder nå “Liste
av Dobbelt
.” Liste
er et generisk grensesnitt, uttrykt som Liste
, det tar en Dobbelt
type argument, som også spesifiseres når du oppretter det faktiske objektet. Kompilatoren kan nå håndheve typekorrekthet når du legger til et objekt i listen - for eksempel kan listen lagres Dobbelt
bare verdier. Denne håndhevelsen fjerner behovet for (Dobbelt)
rollebesetning.
Oppdage generiske typer
EN generisk type er en klasse eller et grensesnitt som introduserer et sett med parametrerte typer via en formell type parameterliste, som er en kommaseparert liste over typeparameternavn mellom et par vinkelparenteser. Generiske typer følger følgende syntaks:
klasse identifikator<formalTypeParameterList> {// class body} -grensesnitt identifikator<formalTypeParameterList> {// grensesnitttekst}
Java Collections Framework tilbyr mange eksempler på generiske typer og deres parameterlister (og jeg henviser til dem gjennom denne artikkelen). For eksempel, java.util.Sett
er en generisk type, er dens formelle parameterliste, og
E
er listens ensomme parameter. Et annet eksempel erjava.util.Kart
.
Navnekonvensjon for Java-typeparameter
Java-programmeringskonvensjon dikterer at typeparameternavn er enkle store bokstaver, for eksempel EN parameterisert type er en generisk type forekomst der den generiske typen typeparametere erstattes med faktiske type argumenter (skriv navn). For eksempel, Java-språket støtter følgende typer faktiske argumentargumenter: Hver generiske type innebærer eksistensen av en rå type, som er en generisk type uten en formell typeparameterliste. For eksempel, Å erklære en generisk type innebærer å spesifisere en formell typeparameterliste og få tilgang til disse typeparametrene gjennom hele implementeringen. Bruk av den generiske typen innebærer å overføre faktiske typeargumenter til typeparametrene når den generiske typen startes. Se liste 1. Oppføring 1 viser generell typedeklarasjon og bruk i sammenheng med en enkel containertype som lagrer objekter av riktig argumenttype. For å holde koden enkel har jeg utelatt feilkontroll. De De Kompilere oppføring 1 ( Vær imidlertid oppmerksom på at det ikke er noen måte å bryte typesikkerheten i dette eksemplet. Det er rett og slett ikke mulig å lagre et ikke- Henrette De Noen ganger vil du begrense typene av faktiske typeargumenter som kan overføres til en typeparameter. For eksempel, kanskje du vil begrense en typeparameter til å bare akseptere Du kan begrense en typeparameter ved å spesifisere en øvre grense, som er en type som fungerer som den øvre grensen for typene som kan sendes som faktiske typeargumenter. Spesifiser øvre grense ved å bruke det reserverte ordet For eksempel, Du kan tilordne mer enn én øvre grense til en typeparameter. Imidlertid må den første grensen alltid være en klasse, og de ekstra grensene må alltid være grensesnitt. Hver avgrensning er skilt fra forgjengeren med et ampersand ( Listing 2’s De De Kompilere oppføring 2 ( Du kan ikke spesifisere en nedre grense for en parameter for generisk type. For å forstå hvorfor jeg anbefaler å lese vanlige spørsmål om Angelika Langers Java Generics om temaet lavere grenser, som hun sier "ville være forvirrende og ikke spesielt nyttig." La oss si at du vil skrive ut en liste over objekter, uansett om disse objektene er strenger, ansatte, figurer eller en annen type. Ditt første forsøk kan se ut som det som er vist i Listing 3. Det virker logisk at en liste over strenger eller en liste over heltall er en undertype av en liste over objekter, men kompilatoren klager når du prøver å kompilere denne oppføringen. Spesielt forteller den deg at en liste-over-streng ikke kan konverteres til en liste-over-objekt, og på samme måte for en liste over heltal. Feilmeldingen du har mottatt, er relatert til den grunnleggende regelen for generiske legemidler:E
for element, K
for nøkkel, V
for verdi, og T
for type. Unngå om mulig å bruke et meningsløst navn som P
— java.util.Liste
betyr en liste over elementer, men hva kan du muligens mene med Liste
Sett
er en parameterisert type hvor String
er det faktiske typeargumentet som erstatter typeparameteren E
.Liste
, Dyr
blir overført til E
.Sett
, Liste
blir overført til E
.Kart
, String
blir overført til K
og Streng []
blir overført til V
.klasse Container {Settelementer; }
, E
blir overført til E
.?
) overføres til typeparameteren. For eksempel i Klasse
, ?
blir overført til T
.Klasse
er den rå typen for Klasse
. I motsetning til generiske typer, kan rå typer brukes med alle slags objekter.Deklarere og bruke generiske typer i Java
Oppføring 1:
GenDemo.java
(versjon 1)klasse Container {private E [] -elementer; privat int indeks; Beholder (int størrelse) {elementer = (E []) nytt objekt [størrelse]; indeks = 0; } void add (E element) {elements [index ++] = element; } Få (int-indeks) {returelementer [indeks]; } int størrelse () {returindeks; }} offentlig klasse GenDemo {public static void main (String [] args) {Container con = new Container (5); con.add ("Nord"); con.add ("Sør"); con.add ("Øst"); con.add ("West"); for (int i = 0; i <con.size (); i ++) System.out.println (con.get (i)); }}
Container
klasse erklærer seg for å være en generisk type ved å spesifisere formell type parameterliste. Type parameter
E
brukes til å identifisere typen lagrede elementer, elementet som skal legges til den interne matrisen, og returtypen når et element hentes.Beholder (int størrelse)
konstruktør oppretter matrisen via elementer = (E []) nytt objekt [størrelse];
. Hvis du lurer på hvorfor jeg ikke spesifiserte elementer = ny E [størrelse];
, årsaken er at det ikke er mulig. Dette kan føre til en ClassCastException
.javac GenDemo.java
). De (E [])
cast får kompilatoren til å sende ut en advarsel om at rollebesetningen ikke er merket av. Det markerer muligheten for at nedkasting fra Gjenstand[]
til E []
kan bryte med typen sikkerhet fordi Gjenstand[]
kan lagre alle typer objekter.E
objekt i den interne matrisen. Prefiksering av Beholder (int størrelse)
konstruktør med @SuppressWarnings ("ukontrollert")
ville undertrykke denne advarselen.java GenDemo
for å kjøre dette programmet. Du bør følge følgende utdata:Nord sør øst vest
Parametere for avgrensningstype i Java
E
i Sett
er et eksempel på en parameter for ubegrenset type fordi du kan overføre et hvilket som helst faktisk argument til E
. For eksempel kan du spesifisere Sett
, Sett
, eller Sett
.Ansatt
og underklassene.strekker
etterfulgt av navnet på den øvre grensen.klasse Ansatte
begrenser typene som kan overføres til Ansatte
til Ansatt
eller en underklasse (f.eks. Regnskapsfører
). Spesifisering nye ansatte
ville være lovlig, mens nye ansatte
ville være ulovlig.&
). Sjekk ut oppføring 2.Oppføring 2:
GenDemo.java
(versjon 2)importere java.math.BigDecimal; importere java.util.Arrays; abstrakt klasse Ansatt {privat BigDecimal timeslønn; privat strengnavn; Ansatt (strengnavn, BigDecimal timelønn) {this.name = navn; this.hourlySalary = hourlySalary; } offentlig BigDecimal getHourlySalary () {return hourlySalary; } public String getName () {return name; } public String toString () {return name + ":" + hourlySalary.toString (); }} klasse Regnskapsfører utvider Ansatt implementerer Sammenlignbar {Regnskapsfører (String navn, BigDecimal hourlySalary) {super (name, hourlySalary); } public int CompareTo (Accountant acct) {return getHourlySalary (). CompareTo (acct.getHourlySalary ()); }} klasse SortedMedarbeidere
Ansatt
klasse trekker ut begrepet arbeidstaker som mottar timelønn. Denne klassen er underklassert av Regnskapsfører
, som også implementerer Sammenlignelig
for å indikere det Regnskapsfører
s kan sammenlignes i henhold til deres naturlige rekkefølge, som tilfeldigvis er timelønn i dette eksemplet.java.lang. sammenlignbar
grensesnitt er erklært som en generisk type med en enkelt type parameter som heter T
. Dette grensesnittet gir en int CompareTo (T o)
metode som sammenligner gjeldende objekt med argumentet (av typen T
), returnerer et negativt heltall, null eller et positivt heltall ettersom dette objektet er mindre enn, lik eller større enn det spesifiserte objektet.Sorterte ansatte
klasse lar deg lagre Ansatt
underklasseforekomster som implementeres Sammenlignelig
i en intern matrise. Denne matrisen er sortert (via java.util.Arrayer
klasse’s void sort (Object [] a, int fromIndex, int toIndex)
klassemetode) i stigende rekkefølge etter timelønnen etter en Ansatt
underklasse forekomst er lagt til.javac GenDemo.java
) og kjør applikasjonen (java GenDemo
). Du bør følge følgende utdata:George Smith: 15.20 Jane Jones: 25,60 John Doe: 35,40
Nedre grenser og generiske parametere
Vurderer jokertegn
Oppføring 3:
GenDemo.java
(versjon 3)importere java.util.ArrayList; importere java.util.Iterator; importere java.util.List; offentlig klasse GenDemo {offentlig statisk tomrom hoved (String [] args) {Liste retninger = ny ArrayList (); retninger.add ("nord"); retninger.add ("sør"); retninger.add ("øst"); retninger.add ("vest"); printList (veibeskrivelse); Listekarakterer = ny ArrayList (); karakterer.add (nytt heltal (98)); karakterer.add (nytt heltal (63)); karakterer.add (nytt heltal (87)); printList (karakterer); } statisk ugyldig printList (Liste liste) {Iterator iter = list.iterator (); mens (iter.hasNext ()) System.out.println (iter.next ()); }}