Programavimas

Tipo priklausomybė „Java“, 2 dalis

Suprasti tipų suderinamumą yra esminis dalykas rašant geras „Java“ programas, tačiau nežinantiesiems „Java“ kalbos elementų skirtumų sąveika gali pasirodyti labai akademinė. Šis dviejų dalių straipsnis skirtas programinės įrangos kūrėjams, pasirengusiems įveikti iššūkį! 1 dalis atskleidė kovariantinius ir prieštaringus ryšius tarp paprastesnių elementų, tokių kaip masyvo tipai ir bendriniai tipai, taip pat specialus „Java“ kalbos elementas, pakaitos simbolis. 2 dalyje nagrinėjama „Java Collections“ API, generinių ir lambda išraiškų priklausomybė nuo tipo.

Šoksime tiesiai, taigi, jei dar neskaitėte 1 dalies, rekomenduoju pradėti nuo ten.

API prieštaringumo pavyzdžiai

Pirmąjį pavyzdį apsvarstykite Lyginamasis versija java.util.Collections.sort (), iš „Java Collections“ API. Šio metodo parašas yra:

  negaliojantis rūšiavimas (sąrašų sąrašas, palyginamasis c) 

rūšiuoti () metodas rūšiuoti bet kurį Sąrašas. Paprastai lengviau naudoti perkrautą versiją su parašu:

 rūšiuoti (sąrašas) 

Tokiu atveju, tęsiasi Palyginamas išreiškia, kad rūšiuoti () gali būti iškviesti tik tuo atveju, jei būtini metodų palyginimo elementai (būtent palyginti su) buvo apibrėžti elemento tipu (arba jo supertipu, dėka ? super T):

 rūšiuoti (integerList); // Sveikasis skaičius įgyvendina Palyginamą rūšiavimą (customerList); // veikia tik tada, jei klientas įgyvendina „Palyginamas“ 

Generinių vaistų naudojimas palyginimui

Akivaizdu, kad sąrašas yra rūšiuojamas tik tuo atveju, jei jo elementus galima palyginti. Palyginimas atliekamas vienu metodu palyginti su, kuris priklauso sąsajai Palyginamas. Privalote įgyvendinti palyginti su elementų klasėje.

Tačiau šio tipo elementus galima rūšiuoti tik vienu būdu. Pavyzdžiui, galite rūšiuoti a Klientas pagal asmens tapatybės dokumentą, bet ne pagal gimtadienį ar pašto kodą. Naudojant Lyginamasis versija rūšiuoti () yra lankstesnis:

 publicstatic void sort (sąrašų sąrašas, palyginamasis c) 

Dabar elementus lyginame ne elemento klasėje, o papildomame Lyginamasis objektas. Šioje bendrojoje sąsajoje yra vienas objekto metodas:

 int palyginti (T o1, T o2); 

Kontraversiški parametrai

Intuituojant objektą daugiau nei vieną kartą, galite rūšiuoti objektus pagal skirtingus kriterijus. Bet ar mums tikrai reikia tokios komplikuotos Lyginamasis tipo parametras? Daugeliu atvejų, Lyginamasis užtektų. Mes galėtume ja naudotis palyginti () metodas palyginti bet kokius du elementus Sąrašas objektas:

klasė DateComparator įgyvendina Comparator {public int palyginti (Data d1, Data d2) {return ...} // lygina du objekto Data objektus} Sąrašas dateList = ...; // Datos objektų sąrašas rūšiuojamas (dateList, new DateComparator ()); // rūšiuoja dataList 

Naudojant sudėtingesnę metodo versiją Collection.sort () tačiau nustatykite mus papildomiems naudojimo atvejams. Prieštaringo tipo parametras Palyginamas leidžia rūšiuoti tipų sąrašą Sąrašas, nes java.util.Data yra supertipas java.sql.Data:

 Sąrašas sqlList = ...; rūšiuoti (sqlList, naujas DateComparator ()); 

Jei praleidome prieštaringumą rūšiuoti () parašas (naudojant tik arba nepatikslinta, nesaugi ), tada kompiliatorius atmeta paskutinę eilutę kaip tipo klaidą.

Norint paskambinti

 rūšiuoti (sqlList, naujas SqlDateComparator ()); 

turėtumėte parašyti papildomą klasę be būdo:

 „Class SqlDateComparator“ pratęsia „DateComparator“ {} 

Papildomi metodai

Kolekcijos.rūšiuoti () nėra vienintelis „Java Collections“ API metodas, turintis prieštaringą parametrą. Tokie metodai kaip Pridėti viską(), binarySearch (), kopija (), užpildyti ()ir pan., gali būti naudojami panašiai lanksčiai.

Kolekcijos metodai kaip maks. () ir min. () siūlome kontraversiškus rezultatų tipus:

 viešoji statiška  T max (kolekcijos kolekcija) {...} 

Kaip matote čia, tipo parametro galima paprašyti, kad jis atitiktų daugiau nei vieną sąlygą, tiesiog naudojant &. pratęsia Object gali pasirodyti nereikalingas, tačiau tai numato maks. () pateikia tipo rezultatą Objektas o ne iš eilės Palyginamas baitų kode. (Baitiniame kode nėra tipo parametrų.)

Perkrauta versija maks. () su Lyginamasis yra dar juokingiau:

 public static T max (Kolekcijos kolekcija, Komparatoriaus komp.) 

Tai maks. () turi tiek kontravariantą ir kovarianto tipo parametrai. Nors elementai Kolekcija turi būti (galbūt skirtingų) tam tikro (aiškiai nenurodyto) tipo potipių, Lyginamasis turi būti išprovokuotas to paties tipo supertipui. Daug reikia iš kompiliatoriaus išvadų algoritmo, kad būtų galima atskirti šį tarpinį tipą nuo tokio skambučio:

 Kolekcijos kolekcija = ...; Palyginamasis lygintuvas = ...; max (kolekcija, lyginamasis); 

Dėžės tipo parametrų įrišimas

Kaip paskutinį „Java Collections“ API priklausomybės nuo tipų ir dispersijos pavyzdį, dar kartą apsvarstykime rūšiuoti () su Palyginamas. Atkreipkite dėmesį, kad jis naudoja abu tęsiasi ir super, kurie yra dėžutėje:

 statinis  negaliojantis rūšiavimas (sąrašų sąrašas) {...} 

Šiuo atveju mes nesidomime nuorodų suderinamumu tiek, kiek privalome pavyzdį. Šis pavyzdys rūšiuoti () metodas rūšiuoja a sąrašą objektas su klasės elementais, įgyvendinančiais Palyginamas. Daugeliu atvejų rūšiavimas veiktų be metodo paraše:

 rūšiuoti (dateList); // java.util.Date įgyvendina Palyginamą rūšiavimą (sqlList); // java.sql.Date įgyvendinama Palyginama 

Apatinė tipo parametro riba suteikia papildomą lankstumą. Palyginamas nebūtinai reikia įdiegti elementų klasėje; pakanka tai įdiegti superklase. Pavyzdžiui:

 klasės „SuperClass“ įgyvendina palyginamąjį {public int palyginti (SuperClass s) {...}} klasę „SubClass“ praplečia „SuperClass“ {} // neperkraunant palyginimo () sąrašo superList = ...; rūšiuoti (superList); Sąrašas subList = ...; rūšiuoti (subList); 

Kompiliatorius priima paskutinę eilutę su

 statinis  negaliojantis rūšiavimas (sąrašų sąrašas) {...} 

ir atmeta jį su

statinis  negaliojantis rūšiavimas (sąrašų sąrašas) {...} 

Šio atmetimo priežastis yra ta, kad tipas Poklasis (kurį sudarytojas nustatytų pagal tipą Sąrašas parametre subList) netinka kaip tipo parametras T tęsiasi Palyginama. Tipas Poklasis neįgyvendina Palyginamas; jis tik įgyvendina Palyginamas. Šie du elementai nesuderinami, nes nėra numanomo kovariantiškumo Poklasis yra suderinamas su „SuperClass“.

Kita vertus, jei mes naudojame , kompiliatorius nesitiki Poklasis įgyvendinti Palyginamas; pakanka jei „SuperClass“ tai daro. Tai pakanka, nes metodas palyginti su() yra paveldėtas iš „SuperClass“ ir gali būti pareikalauta Poklasis objektai: tai išreiškia prieštaringai.

Prieštaringi tipo parametrų kintamieji

Viršutinė arba apatinė riba taikoma tik tipo parametras akimirkų, nurodytų kovariška ar prieštaringa nuoroda. Jeigu Bendra kovariuota nuoroda; ir Bendroji sutartinė nuoroda;, galime sukurti ir nurodyti skirtingų objektų objektus Bendrasis akimirksniai.

Metodo parametrui ir rezultato tipui galioja skirtingos taisyklės (pvz., įvestis ir produkcija parametrų tipai). Savavališkas objektas, suderinamas su Potipis gali būti perduotas kaip metodo parametras rašyti (), kaip apibrėžta aukščiau.

 contravariantReference.write (new SubType ()); // Gerai contravariantReference.write (new SubSubType ()); // OK too contravariantReference.write (new SuperType ()); // tipo klaida ((Generic) contravariantReference) .write (nauja „SuperType“); // GERAI 

Dėl prieštaringumo galima perduoti parametrą rašyti (). Tai prieštarauja kovariantiniam (taip pat ir neribotam) pakaitos simboliui.

Rezultatų tipo situacija nesikeičia įrišant: skaityti () vis tiek pateikia tipo rezultatą ?, suderinamas tik su Objektas:

 Objektas o = contravariantReference.read (); SubType st = contravariantReference.read (); // tipo klaida 

Paskutinė eilutė sukelia klaidą, nors mes deklaravome a nuoroda tipo Bendrasis.

Rezultato tipas yra suderinamas su kitu tipu tik po to nuorodos tipas buvo aiškiai konvertuotas:

 SuperSuperType sst = ((Generic) contravariantReference) .read (); sst = („SuperSuperType“ contravariantReference.read (); // nesaugi alternatyva 

Ankstesnių sąrašų pavyzdžiai rodo, kad skaitymo ar rašymo prieiga prie tipo kintamojo parametras elgiasi taip pat, nepaisant to, ar tai vyksta per metodą (skaityti ir rašyti), ar tiesiogiai (duomenys pavyzdžiuose).

Skaitymas ir rašymas pagal tipo parametrų kintamuosius

1 lentelėje parodyta, kad skaitymas į Objektas kintamasis visada įmanomas, nes kiekviena klasė ir pakaitos simboliai yra suderinami su Objektas. Rašymas Objektas yra įmanoma tik naudojant kontraversišką nuorodą po tinkamo liejimo, nes Objektas nėra suderinamas su pakaitos simboliu. Skaitymas be metimo į netinkamą kintamąjį yra įmanomas naudojant kovariantinę nuorodą. Rašyti galima su prieštaringa nuoroda.

1 lentelė. Tipo parametro kintamųjų skaitymo ir rašymo prieiga

skaitymas

(įvestis)

skaityti

Objektas

rašyti

Objektas

skaityti

supertipas

rašyti

supertipas

skaityti

potipis

rašyti

potipis

Pakaitos simbolis

?

Gerai Klaida Vaidina Vaidina Vaidina Vaidina

Kovariantas

? tęsiasi

Gerai Klaida Gerai Vaidina Vaidina Vaidina

Kontravariantas

super

Gerai Vaidina Vaidina Vaidina Vaidina Gerai

1 lentelės eilutėse nurodoma rūšies nuorodair stulpeliai į duomenų tipas turi būti prieinama. Antraštėse „supertype“ ir „subtype“ nurodomos pakaitos ribos. Įrašas „aktoriai“ reiškia, kad nuoroda turi būti perduota. „Gerai“ paskutiniuose keturiuose stulpeliuose nurodomi tipiški kovariacijos ir prieštaringumo atvejai.

Žr. Šio straipsnio pabaigoje pateiktą lentelės sisteminę bandymų programą su išsamiais paaiškinimais.

Objektų kūrimas

Viena vertus, negalima kurti pakaitos tipo objektų, nes jie yra abstraktūs. Kita vertus, galite sukurti tik neriboto pakaitos tipo masyvo objektus. Tačiau negalite kurti kitų bendrų pavyzdžių objektų.

 Generic [] genericArray = naujas Generic [20]; // tipo klaida Generic [] wildcardArray = new Generic [20]; // Gerai genericArray = (Generic []) pakaitos simbolis; // nepatikrinta konversija genericArray [0] = new Generic (); genericArray [0] = nauja Generic (); // tipo klaida wildcardArray [0] = new Generic (); // GERAI 

Dėl masyvų kovariacijos pakaitos masyvo tipas Bendras [] yra visų pavyzdžių masyvo tipo supertipas; todėl galimas priskyrimas aukščiau minėto kodo paskutinėje eilutėje.

Bendroje klasėje negalime sukurti tipo parametro objektų. Pavyzdžiui, konstruktoriuje „ArrayList“ masyvo objektas turi būti tipo Objektas [] sukūrus. Tada galime jį konvertuoti į tipo parametro masyvo tipą:

 klasės „MyArrayList“ įgyvendina „List {private final E []“ turinį; „MyArrayList“ (int dydis) {content = new E [dydis]; // tipo klaidos turinys = (E []) naujas objektas [dydis]; // sprendimas} ...} 

Jei norite saugesnio sprendimo, praleiskite Klasė faktinio tipo parametro vertė konstruktoriui:

 turinys = (E []) java.lang.reflect.Array.newInstance(„myClass“, dydis); 

Kelių tipų parametrai

Bendrasis tipas gali turėti daugiau nei vieną tipo parametrą. Tipo parametrai nekeičia kovariacijos ir prieštaringumo elgesio, todėl kartu gali atsirasti keli tipų parametrai, kaip parodyta žemiau:

 G klasės {} G nuoroda; nuoroda = naujas G (); // be dispersijos nuorodos = naujas G (); // su bendrai ir prieštaringai 

Bendroji sąsaja java.util.Žemėlapis yra dažnai naudojamas kaip kelių tipų parametrų pavyzdys. Sąsajoje yra du tipo parametrai: vienas raktui ir kitas reikšmei. Naudinga objektus susieti su raktais, pavyzdžiui, kad galėtume lengviau juos rasti. Telefonų knyga yra pavyzdys Žemėlapis objektas, naudojant kelis tipų parametrus: abonento vardas yra raktas, telefono numeris yra vertė.

Sąsajos įgyvendinimas java.util.HashMap turi konstruktorių savavališkai konvertuoti Žemėlapis objektas į susiejimo lentelę:

 viešasis „HashMap“ (žemėlapis m) ... 

Dėl kovariacijos šiuo atveju parametro objekto tipo parametras neturi atitikti tikslios tipo parametrų klasės K. ir V. Vietoj to, jis gali būti pritaikytas kovarsija:

 Žemėlapio klientai; ... kontaktai = naujas „HashMap“ (klientai); // kovariantas 

Čia Id yra supertipas Kliento numerisir Asmuo yra supertipas Klientas.

Metodų variacija

Mes kalbėjome apie tipų dispersiją; dabar pereikime prie kiek lengvesnės temos.