Ohjelmointi

JDK 7: Timanttioperaattori

Project Coin tarjoaa lukuisia "pieniä kielen parannuksia" uusien JDK 7 -ominaisuuksien osajoukkona. Kirjoitin äskettäin blogia Project Coinin kytkemisestä jousiin ja kirjoitan tässä viestissä uudesta Diamond Operatorista ().

Timanttioperaattori vähentää joitain Java: n yleisistä yleisistä geneerikoista antamalla kääntäjän päättää parametrityypeistä yleisluokkien rakentajille. Alkuperäinen ehdotus Diamond-operaattorin lisäämisestä Java-kielelle tehtiin helmikuussa 2009, ja se sisältää tämän yksinkertaisen esimerkin:

Harkitse esimerkiksi seuraavaa tehtävälausetta:

Kartta anagrams = uusi HashMap();

Tämä on melko pitkä, joten se voidaan korvata tällä:

Kartta anagrammat = uusi HashMap ();

Yllä oleva Jeremy Mansonin ehdotuksessa annettu esimerkki (joka oli yksi ensimmäisistä vastauksina Project Coin -ideoiden pyyntöön) on yksinkertainen, mutta osoittaa riittävästi, kuinka timanttioperaattoria sovelletaan JDK 7: ssä. Mansonin ehdotus tarjoaa myös merkittävää, miksi tämä lisäys oli toivottavaa:

Vaatimus, jonka mukaan tyypin parametrit on kopioitava tarpeettomasti, kuten

tämä rohkaisee valitettavaa

staattisten tehdasmenetelmien ylikuormitus yksinkertaisesti tyypin päättelyn vuoksi

toimii menetelmien kutsumisessa.

Toisin sanoen timanttioperaattorin JDK 7 -projektirahojen lisääminen tuo rakentajille tyypin johtopäätöksen, joka on ollut käytettävissä menetelmillä. Menetelmillä tyypin päättely tehdään implisiittisesti, kun jätetään pois nimenomainen parametrityypin määritys. Toisaalta timanttioperaattori on toisaalta määriteltävä nimenomaisesti, jotta "käsketään" kääntäjää päättelemään tyyppi.

Alkuperäisessä ehdotuksessaan Manson huomauttaa, että syntaksia ilman erityistä timanttioperaattoria ei voitu käyttää implisiittisesti päätelmiin tyyppeistä, koska "taaksepäin yhteensopivuuden vuoksi uusi Map () osoittaa raakatyypin, joten sitä ei voida käyttää tyyppiin päättely." Java-opetusohjelmien Java-kielen oppimisen Generics Lesson -tyyppisivut sisältävät sivun nimeltä "Type Inference and Instantiation of Generic Classes", joka on jo päivitetty vastaamaan Java SE 7: ää. Tässä osassa kuvataan myös, miksi operaattori on määritettävä ilmoittamaan kääntäjälle nimenomaisesti, että hän käyttää tyypin päättelyä instantioinnissa:

Huomaa, että sinun on määritettävä timanttioperaattori, jotta voit hyödyntää tyyppien automaattista päätelmää yleisen luokan ennakoinnin aikana. Seuraavassa esimerkissä kääntäjä luo tarkistamattoman muunnosvaroituksen, koska HashMap () -konstruktori viittaa HashMap-raakatyyppiin, ei Mapiin tyyppi

Tehokkaan Javan toisen painoksen kohdassa 24 ("Poista tarkoittamattomat varoitukset") Josh Bloch korostaa lihavoitu teksti "Poista kaikki mahdolliset tarkistamattomat varoitukset." Bloch näyttää esimerkin tarkistamattomasta muunnosvaroituksesta, joka tapahtuu, kun käännetään koodia, joka käyttää raakatyyppiä ilmoituksen oikealla puolella. Seuraava koodiluettelo näyttää koodin, joka johtaa tähän varoitukseen.

lopullinen kartta statesToCities = uusi HashMap (); // raaka! 

Seuraavat kaksi näytön tilannekuvaa näyttävät kääntäjän vastauksen yllä olevaan koodiriviin. Ensimmäinen kuva näyttää viestin, kun -Xlint-varoituksia ei ole otettu käyttöön, ja toinen näyttää selkeämmän varoituksen, joka tapahtuu, kun -Xlint: ei tarkastettu annetaan argumenttina javacille.

Jos Tehokas Java, Bloch huomauttaa, että tämä tarkkailematon varoitus on helppo käsitellä antamalla parametrityyppi nimenomaisesti yleisluokan ilmentymälle. JDK 7: n kanssa tämä on vieläkin helpompaa! Sen sijaan, että tarvitsisi lisätä nimenomaista tekstiä näillä tyyppinimillä, tyypit voidaan päätellä monissa tapauksissa, ja timanttioperaattorin määrittely käskee kääntäjää tekemään tämän päätelmän raakatyypin käyttämisen sijaan.

Seuraava Java-koodiluettelo tarjoaa yksinkertaistettuja esimerkkejä näistä käsitteistä. On olemassa menetelmiä, jotka osoittavat raakaryhmän välittömän ilmentämisen, joukon välittömän ilmentämisen sen parametrityypin nimenomaisella määrittelyllä ja ryhmän ilmentämisen parametrityypillä johtuen timanttioperaattorin määrittelystä ().

pakkaus pölyä.esimerkkejä; tuo java.util.HashMap; tuo java.util.HashSet; tuo java.util.Kartta; tuo java.util.Set; tuo staattinen java.lang.System.out; / ** * Hyvin yksinkertainen esittely JDK 7: n / Project Coinin "Diamond Operatorista". * / public class DiamondOperatorDemo {/ ** "Raaka" -tyypin käyttö. * / yksityinen staattinen joukko rawWithoutExplicitTyping () {lopulliset joukon nimet = uusi HashSet (); addNames (nimet); palata nimet; } / ** Määritetään nimenomaisesti yleisluokan instantiation-parametrityyppi. * / yksityinen staattinen joukko explicitTypingExplicitlySpecified () {lopullisten joukkojen nimet = uusi HashSet (); addNames (nimet); palata nimet; } / ** * Johdetaan yleisluokan instantiation-parametrityyppi JDK 7: n * 'Diamond Operatorilla'. * / yksityinen staattinen joukko explicitTypingInferredWithDiamond () {lopullisten joukkojen nimet = uusi HashSet (); addNames (nimet); palata nimet; } private static void addNames (lopulliset joukon nimetToAddTo) {nimetToAddTo.add ("Dustin"); namesToAddTo.add ("Rett"); namesToAddTo.add ("Homer"); } / ** * Suoritettava päätoiminto. * / public static void main (viimeiset merkkijono [] argumentit) {out.println (rawWithoutExplicitTyping ()); out.println (explicitTypingExplicitlySpecified ()); out.println (explicitTypingInferredWithDiamond ()); }} 

Kun yllä oleva koodi kootaan, vain "raaka" tapaus johtaa varoitukseen.

Tässä vaiheessa voi olla oivaltavaa katsoa, ​​mitä javap kertoo meille näistä kolmesta menetelmästä. Tämä tehdään tässä tapauksessa komennolla (-v vaihtoehto sanalliselle antaa kaikki mehukkaat yksityiskohdat ja -p näyttää nämä mehukkaat tiedot yksityinen menetelmät):

javap -v -p -luokkapoluluokat dustin.examples.DiamondOperatorDemo 

Koska nämä menetelmät olivat kaikki yhdessä luokassa, koko luokassa on yksi tuotosvirta. Kuitenkin niiden vertailun helpottamiseksi olen leikannut ja liittänyt lähdön muotoon, joka tasaa kunkin menetelmän javap-lähdön toisiaan vastaan. Jokainen sarake edustaa javap yhden menetelmän tulos. Olen muuttanut tietyn menetelmän kirjasimen värin siniseksi, jotta se erottuu ja merkitsen kyseisen sarakkeen tuotoksen.

Muut kuin itse menetelmien nimet, menetelmässä ei ole mitään eroa javap ulostulo. Tämä johtuu siitä, että Java-generiikkatyyppien poisto tarkoittaa, että tyyppiin perustuva erottelu ei ole käytettävissä ajon aikana. Java-opetusohjelma Generics sisältää sivun nimeltä Type Erasure, joka selittää tämän:

Kääntäjä poistaa kaikki tiedot todellisesta tyyppiargumentista kääntöhetkellä.

Tyyppipoisto on olemassa, jotta uusi koodi voi jatkaa toimintaansa vanhan koodin kanssa. Raakatyypin käyttöä mistä tahansa muusta syystä pidetään huonoina ohjelmointikäytäntöinä, ja sitä tulisi välttää aina kun mahdollista.

Kuten yllä oleva laina muistuttaa, poistaminen tarkoittaa, että raakatyypin tavu koodata ei ole eroa kuin nimenomaisesti kirjoitettu parametrityyppi, mutta rohkaisee myös kehittäjiä olemaan käyttämättä raakatyyppejä paitsi integroimalla vanhaan koodiin.

Johtopäätös

Timanttioperaattorin sisällyttäminen) tarkoittaa Java SE 7: ssä, että geneeristen luokkien instantisoiva koodi voi olla vähemmän yksityiskohtainen. Koodaamiskielet yleensä ja erityisesti Java ovat siirtymässä kohti ideoita, kuten kokoonpanoa koskeva käytäntö, kokoonpano poikkeuksellisesti ja päätellä asioita mahdollisimman usein sen sijaan, että ne vaativat nimenomaista määrittelyä. Dynaamisesti kirjoitetut kielet ovat tunnettuja tyypin päättelystä, mutta jopa staattisesti kirjoitettu Java voi tehdä enemmän kuin se, ja timanttioperaattori on esimerkki tästä.

Alkuperäinen julkaisu on saatavilla osoitteessa //marxsoftware.blogspot.com/

Tämän tarinan "JDK 7: The Diamond Operator" julkaisi alun perin JavaWorld.