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
| Gerai | Klaida | Gerai | Vaidina | Vaidina | Vaidina |
Kontravariantas
| 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 numeris
ir Asmuo
yra supertipas Klientas
.
Metodų variacija
Mes kalbėjome apie tipų dispersiją; dabar pereikime prie kiek lengvesnės temos.