Programiranje

Odvisnost od tipa v Javi, 2. del

Razumevanje združljivosti tipov je bistvenega pomena za pisanje dobrih programov Java, toda medsebojno vplivanje na razlike med elementi jezika Java se lahko nepoznavalcem zdi zelo akademsko. Ta dvodelni članek je namenjen razvijalcem programske opreme, ki so pripravljeni spoprijeti se z izzivom! Prvi del je razkril kovariante in kontravariantne povezave med enostavnejšimi elementi, kot so tipi matrike in generični tipi, pa tudi posebni element jezika Java, nadomestni znak. Drugi del raziskuje odvisnost tipa v API-ju Java Collections, v generikih in lambda izrazih.

Skočili bomo takoj, tako da, če še niste prebrali 1. dela, priporočam, da začnete tam.

Primeri API za kontravariance

Za naš prvi primer si oglejte Primerjalnik različica java.util.Collections.sort (), iz Java Collections API. Podpis te metode je:

  void sort (seznam seznamov, primerjalnik c) 

The razvrsti () metoda razvrsti katero koli Seznam. Običajno je lažje uporabiti preobremenjeno različico s podpisom:

 razvrsti (Seznam) 

V tem primeru, razširja Primerljivo izraža, da razvrsti () se lahko pokliče le, če so potrebni elementi za primerjavo metod (in sicer primerjaj) so bili definirani v tipu elementa (ali v njegovem nadtipu, zahvaljujoč ? super T):

 sort (integerList); // Integer izvaja primerljivo razvrščanje (customerList); // deluje samo, če kupec izvaja Primerljivo 

Uporaba generičnih zdravil za primerjavo

Očitno je, da je seznam mogoče razvrstiti le, če je mogoče njegove elemente med seboj primerjati. Primerjava se opravi z eno samo metodo primerjajTo, ki pripada vmesniku Primerljivo. Morate izvesti primerjajTo v razredu elementov.

To vrsto elementa pa je mogoče razvrstiti samo na en način. Na primer, lahko razvrstite a Stranka po osebnem dokumentu, ne pa tudi po rojstnem dnevu ali poštni številki. Uporabljati Primerjalnik različica razvrsti () je bolj prilagodljiv:

 publicstatic void sort (seznam seznamov, primerjalnik c) 

Zdaj elemente primerjamo ne v razredu elementa, ampak v dodatnem Primerjalnik predmet. Ta generični vmesnik ima eno objektno metodo:

 int primerjava (T o1, T o2); 

Parametri kontravarijantov

Instanciranje predmeta večkrat vam omogoča razvrščanje predmetov z uporabo različnih meril. Toda ali res potrebujemo tako zapleteno Primerjalnik parameter tipa? V večini primerov, Primerjalnik bi bilo dovolj. Lahko bi uporabili njegovo primerjaj () metoda za primerjavo katerih koli dveh elementov v Seznam predmet, kot sledi:

razred DateComparator izvaja primerjalnik {public int compare (Date d1, Date d2) {return ...} // primerja oba predmeta Date} Seznam dateList = ...; // Seznam predmetov Date sort (dateList, new DateComparator ()); // razvrsti dateList 

Uporaba bolj zapletene različice metode Collection.sort () vendar nas pripravite na dodatne primere uporabe. Parameter kontravariantnega tipa za Primerljivo omogoča razvrščanje seznama vrst Seznam, Ker java.util.Date je supertip java.sql.Date:

 Seznam sqlList = ...; razvrsti (sqlList, nov DateComparator ()); 

Če izpustimo kontravariance v razvrsti () podpis (samo z uporabo ali neopredeljeno, nevarno ), nato pa prevajalnik zavrne zadnjo vrstico kot napako tipa.

Da bi poklicali

 razvrsti (sqlList, nov SqlDateComparator ()); 

morali bi napisati dodaten razred brez značilnosti:

 razred SqlDateComparator podaljša DateComparator {} 

Dodatne metode

Collections.sort () ni edina metoda API Java Collections, opremljena s kontravariantnim parametrom. Metode kot addAll (), binarySearch (), kopirati(), fill (), in tako naprej, se lahko uporabljajo s podobno prilagodljivostjo.

Zbirke metode, kot so največ () in min () ponujajo kontravariantne vrste rezultatov:

 javni statični  T max (zbirka zbirk) {...} 

Kot vidite tukaj, lahko od parametra tipa zahtevamo, da izpolnjuje več pogojev, samo z uporabo &. The razširi Object morda zdi odveč, vendar to določa največ () vrne rezultat tipa Predmet in ne vrstice Primerljivo v bytecode. (V bajtkodi ni parametrov tipa.)

Preobremenjena različica največ () s Primerjalnik je še bolj smešno:

 javni statični T max (zbirka zbirk, primerjalnik comp) 

To največ () ima oboje kontravariantno in parametri kovariantnega tipa. Medtem ko so elementi Zbirka mora biti (po možnosti drugačnih) podtipov določene (ne izrecno podane) vrste, Primerjalnik mora biti primer za nadtip istega tipa. Za razlikovanje tega vmesnega tipa od takšnega klica je potrebno veliko od algoritma sklepanja prevajalnika:

 Zbirka zbirke = ...; Primerjalnik primerjalnik = ...; max (zbirka, primerjalnik); 

Vezava tipskih parametrov v škatli

Kot naš zadnji primer odvisnosti od tipa in variance v API-ju Java Collections, ponovno razmislimo o podpisu razvrsti () s Primerljivo. Upoštevajte, da uporablja oboje podaljša in super, ki so zapakirani:

 statično  neveljavno razvrščanje (seznam seznamov) {...} 

V tem primeru nas združljivost referenc ne zanima tako zelo kot zavezujoča instancacija. Ta primer razvrsti () metoda razvrsti a seznam objekt z elementi izvedbe razreda Primerljivo. V večini primerov bi razvrščanje delovalo brez v podpisu metode:

 razvrsti (dateList); // java.util.Date izvaja primerljivo razvrščanje (sqlList); // java.sql.Date izvaja Primerljivo 

Spodnja meja parametra tipa pa omogoča dodatno prilagodljivost. Primerljivo ni nujno, da je treba implementirati v razred elementov; dovolj je, da smo ga implementirali v superrazred. Na primer:

 razred SuperClass implementira Primerljiv {public int compareTo (SuperClass s) {...}} razred SubClass razširja SuperClass {} // brez preobremenitve s primerjavo () Seznam superList = ...; razvrsti (superList); SubList seznama = ...; razvrsti (podlist); 

Prevajalnik sprejme zadnjo vrstico z

 statično  neveljavno razvrščanje (seznam seznamov) {...} 

in jo zavrne z

statično  void sort (Seznam seznamov) {...} 

Razlog za to zavrnitev je, da je tip Podrazred (ki bi ga prevajalnik določil glede na vrsto Seznam v parametru subList) ni primeren kot parameter tipa za T se razteza Primerljivo. Tip Podrazred se ne izvaja Primerljivo; samo izvaja Primerljivo. Oba elementa sicer nista združljiva zaradi pomanjkanja implicitne kovarijance Podrazred je združljiv z SuperClass.

Po drugi strani pa, če uporabimo , prevajalnik ne pričakuje Podrazred za izvajanje Primerljivo; dovolj je, če SuperClass naredi to. Dovolj je, ker metoda compareTo () je podedovan od SuperClass in se lahko zahteva Podrazred predmeti: izraža to, kar povzroča kontravarenco.

Kontravariantno dostopanje do spremenljivk parametra tipa

Zgornja ali spodnja meja velja samo za parameter tipa primerov, ki jih napoti kovariantna ali kontravariantna referenca. V primeru Generična kovariantna referenca; in Generični kontravariantReference;, lahko ustvarimo in napotimo predmete različnih Splošno instanci.

Za parameter in vrsto rezultata metode veljajo različna pravila (na primer za vhod in izhod vrste parametrov generičnega tipa). Poljuben objekt, združljiv z Podtip se lahko posreduje kot parameter metode piši (), kot je opredeljeno zgoraj.

 contravariantReference.write (nov podtip ()); // V redu contravariantReference.write (nov SubSubType ()); // V redu tudi kontravariantReference.write (nov SuperType ()); // napaka tipa ((Generic) contravariantReference) .write (new SuperType ()); // V REDU 

Zaradi kontravariance lahko parametru posredujemo piši (). To je v nasprotju s kovarijantnim (tudi neomejenim) nadomestnim tipom.

Situacija se za vrsto rezultata ne spremeni z vezavo: preberi () še vedno daje rezultat tipa ?, združljiv samo z Predmet:

 Objekt o = contravariantReference.read (); Podtip st = contravariantReference.read (); // napaka tipa 

Zadnja vrstica povzroči napako, čeprav smo razglasili a contravariantReference vrste Splošno.

Vrsta rezultata je združljiva z drugo vrsto šele po vrsta reference je bila izrecno pretvorjena:

 SuperSuperType sst = ((Generic) contravariantReference) .read (); sst = (SuperSuperType) contravariantReference.read (); // nevarnejša alternativa 

Primeri v prejšnjih navedbah kažejo, da ima dostop do branja ali pisanja spremenljivko tipa parameter se obnaša enako, ne glede na to, ali se to zgodi prek metode (branje in pisanje) ali neposredno (podatki v primerih).

Branje in pisanje spremenljivk tipa parameter

Tabela 1 prikazuje, da je branje v Predmet spremenljivke je vedno mogoče, ker so vsi razredi in nadomestni znaki združljivi z Predmet. Pisanje Predmet je mogoča le nad kontravariantno referenco po ustreznem ulivanju, ker Predmet ni združljiv z nadomestnim znakom. Branje brez oddajanja v neprimerno spremenljivko je možno s kovarijantno referenco. Pisanje je možno s kontravariantno referenco.

Tabela 1. Dostop do branja in pisanja spremenljivk parametra tipa

branje

(vnos)

preberite

Predmet

piši

Predmet

preberite

supertip

piši

supertip

preberite

podtip

piši

podtip

Nadomestni znak

?

v redu Napaka Igralska zasedba Igralska zasedba Igralska zasedba Igralska zasedba

Kovariantno

? se razteza

v redu Napaka v redu Igralska zasedba Igralska zasedba Igralska zasedba

Kontravariant

? super

v redu Igralska zasedba Igralska zasedba Igralska zasedba Igralska zasedba v redu

Vrstice v tabeli 1 se nanašajo na nekakšna referencain stolpci v vrsta podatkov do katerega lahko dostopate. Naslovi "nadtip" in "podtip" označujejo meje nadomestnih znakov. Vnos "oddaja" pomeni, da je treba sklic oddati. Primer "OK" v zadnjih štirih stolpcih se nanaša na tipične primere za kovarianco in kontravarianco.

Glejte konec tega članka za sistematični preskusni program za tabelo s podrobnimi razlagami.

Ustvarjanje predmetov

Po eni strani ne morete ustvariti predmetov z nadomestnimi znaki, ker so abstraktni. Po drugi strani pa lahko ustvarite niza predmetov samo neomejenega nadomestnega tipa. Vendar ne morete ustvariti predmetov drugih generičnih primerkov.

 Generic [] genericArray = novo Generic [20]; // napaka tipa Generic [] wildcardArray = new Generic [20]; // V redu genericArray = (Generic []) wildcardArray; // nepreverjena pretvorba genericArray [0] = new Generic (); genericArray [0] = novo Generic (); // tip napake wildcardArray [0] = novo Generic (); // V REDU 

Zaradi kovariacije nizov je vrsta nadomestnega polja Splošno [] je nadtip vrste matrike vseh primerkov; zato je dodelitev v zadnji vrstici zgornje kode mogoča.

Znotraj generičnega razreda ne moremo ustvariti predmetov parametra tipa. Na primer, v konstruktorju datoteke ArrayList izvedbe, mora biti objekt polja tipa Predmet [] ob ustvarjanju. Nato ga lahko pretvorimo v vrsto polja parametra tipa:

 razred MyArrayList izvaja vsebino List {private final E []; MyArrayList (int size) {content = new E [size]; // vrsta napake vsebina = (E []) nov objekt [velikost]; // rešitev} ...} 

Za varnejšo zaobidejo mimo Razred vrednost dejanskega parametra tipa za konstruktor:

 content = (E []) java.lang.reflect.Array.newInstance(myClass, velikost); 

Več parametrov tipa

Splošni tip ima lahko več kot en parameter tipa. Parametri tipa ne spreminjajo vedenja kovarijance in kontravariance in več parametrov tipa se lahko pojavi skupaj, kot je prikazano spodaj:

 razred G {} referenca G; sklic = nov G (); // brez referenc variance = novo G (); // s ko- in kontravariancem 

Splošni vmesnik java.util.Map se pogosto uporablja kot primer za parametre več vrst. Vmesnik ima dva parametra tipa, enega za ključ in enega za vrednost. Predmete je koristno povezati s ključi, na primer, da jih bomo lažje našli. Telefonski imenik je primer a Zemljevid objekt, ki uporablja parametre več vrst: ime naročnika je ključ, telefonska številka je vrednost.

Izvedba vmesnika java.util.HashMap ima konstruktor za pretvorbo poljubnega Zemljevid objekt v asociacijsko tabelo:

 javni HashMap (zemljevid m) ... 

Zaradi kovariancije parametru tipa objekta parametra v tem primeru ni treba ustrezati natančnim razredom parametrov tipa K in V. Namesto tega ga je mogoče prilagoditi s kovarianco:

 Kupci zemljevidov; ... kontakti = novi HashMap (kupci); // kovariantno 

Tukaj, Id je supertip Številka kupca, in Oseba je supertip za Stranka.

Različnost metod

Govorili smo o različnosti vrst; zdaj se obrnimo na nekoliko lažjo temo.

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