Uvod u projekt Amber

1. Što je projekt Amber

Project Amber trenutna je inicijativa programera Jave i OpenJDK-a, čiji je cilj unijeti neke male, ali bitne promjene u JDK kako bi proces razvoja bio ljepši. To traje od 2017. godine i već je unijelo neke promjene u Javu 10 i 11, dok su druge predviđene za uključivanje u Javu 12, a još ih dolazi u budućim izdanjima.

Sva su ažuriranja zapakirana u obliku JEP-ova - sheme prijedloga za poboljšanje JDK.

2. Dostavljena ažuriranja

Do danas je Project Amber uspješno izvršio neke promjene u trenutno objavljenim verzijama JDK - JEP-286 i JEP-323.

2.1. Lokalno zaključivanje tipa varijable

Java 7 predstavila je Diamond Operator kao način za olakšavanje rada s generičkim lijekovima. Ova značajka znači da više ne trebamo više puta upisivati ​​generičke podatke u istu izjavu kada definiramo varijable:

Nizovi popisa = novi ArrayList (); // Java 6 Popis nizova = novi ArrayList (); // Java 7

Java 10 obuhvaćao je dovršeni rad na JEP-286, omogućavajući da naš Java kôd definira lokalne varijable bez potrebe za dupliciranjem podataka o tipu gdje god ih prevodilac već ima na raspolaganju. To se u široj zajednici naziva var i donosi sličnu funkcionalnost Javi koja je dostupna na mnogim drugim jezicima.

Ovim radom, kad god definiramo lokalnu varijablu, možemo koristiti var ključna riječ umjesto pune definicije tipa, a kompajler će automatski izraditi ispravne informacije o tipu koje će se koristiti:

var nizovi = novi ArrayList ();

U gore navedenom, varijabla žice utvrđeno je da je tipa ArrayList (), ali bez potrebe za dupliciranjem podataka na istom retku.

To možemo koristiti bilo gdje gdje koristimo lokalne varijable, bez obzira na to kako je vrijednost utvrđena. To uključuje vrste i izraze povratka, kao i jednostavne zadatke poput gore navedenih.

Riječ var je poseban slučaj, jer to nije rezervirana riječ. Umjesto toga, to je posebno ime tipa. To znači da je riječ moguće koristiti za druge dijelove koda - uključujući nazive varijabli. Preporučuje se da to ne činite kako biste izbjegli zabunu.

Zaključivanje lokalnog tipa možemo koristiti samo kada pružimo stvarni tip kao dio deklaracije. Namjerno je dizajniran da ne radi kad je vrijednost izričito null, kada uopće nije navedena vrijednost ili kada navedena vrijednost ne može odrediti točan tip - na primjer, Lambda definicija:

var unknownType; // Nije navedena vrijednost za zaključivanje tipa iz var nullType = null; // Navedena je eksplicitna vrijednost, ali ona je null var lambdaType = () -> System.out.println ("Lambda"); // Lambda bez definiranja sučelja

Međutim, vrijednost može biti null ako je to povratna vrijednost iz nekog drugog poziva budući da sam poziv pruža informacije o tipu:

Izborno ime = Izborno.empty (); var nullName = name.orElse (null);

U ovom slučaju, nullName zaključit će o tipu Niz jer je to vrsta povrata name.orElse () je.

Ovako definirane varijable mogu imati bilo koje druge modifikatore na isti način kao i bilo koja druga varijabla - na primjer, prijelazni, sinkronizirani, i konačni.

2.2. Lokalno zaključivanje tipa varijable za lambde

Gore navedeni rad omogućuje nam deklariranje lokalnih varijabli bez potrebe za dupliciranjem podataka o tipu. Međutim, to ne djeluje na popisima parametara, a posebno ne na parametrima za lambda funkcije, što može izgledati iznenađujuće.

U Javi 10 Lambda funkcije možemo definirati na jedan od dva načina - eksplicitnim deklariranjem tipova ili njihovim potpuno izostavljanjem:

names.stream () .filter (Ime niza -> name.length ()> 5) .map (name -> name.toUpperCase ());

Ovdje drugi redak ima eksplicitnu deklaraciju tipa - Niz - dok ga treći redak potpuno izostavlja, a kompajler izrađuje ispravan tip. Ono što ne možemo je koristiti var Upiši ovdje.

Java 11 omogućuje da se to dogodi, pa umjesto toga možemo napisati:

names.stream () .filter (var ime -> ime.dužina ()> 5) .map (ime var -> ime.toUpperCase ());

To je onda u skladu s upotrebom var unesite negdje drugdje u našem kodu.

Lambde su nas uvijek ograničavale na upotrebu punih imena tipova bilo za svaki parametar, bilo za bilo koji od njih. To se nije promijenilo i korištenje var mora biti za svaki parametar ili nijedan od njih:

numbers.stream () .reduce (0, (var a, var b) -> a + b); // Važeći brojevi.stream () .reduce (0, (var a, b) -> a + b); // Nevažeći brojevi.stream () .reduce (0, (var a, int b) -> a + b); // Nevaljano

Ovdje je prvi primjer savršeno valjan - jer se koriste oba lambda parametra var. Međutim, drugi i treći su nezakoniti jer se koristi samo jedan parametar var, iako u trećem slučaju imamo i eksplicitno ime tipa.

3. Neposredna ažuriranja

Pored ažuriranja koja su već dostupna u izdanim JDK-ovima, nadolazeće izdanje JDK 12 uključuje jedno ažuriranje - JEP-325.

3.1. Prebaci izraze

JEP-325 donosi podršku za pojednostavljivanje načina na koji sklopka izjave djeluju i dopuštaju da se koriste kao izrazi čak i dodatno pojednostaviti kod koji ih koristi.

Trenutno je sklopka Izjava djeluje na vrlo sličan način kao oni na jezicima kao što su C ili C ++. Te promjene čine ga mnogo sličnijim kada izjava u Kotlinu ili podudaranje izjava u Scali.

Uz ove promjene, sintaksa za definiranje naredbe switch izgleda slična sinhronizaciji lambda, uz upotrebu -> simbol. Ovo se nalazi između podudaranja slučaja i koda koji treba izvršiti:

prekidač (mjesec) {case VELJAČA -> System.out.println (28); slučaj APRIL -> System.out.println (30); slučaj LIPANJ -> System.out.println (30); slučaj SEPTEMBAR -> System.out.println (30); slučaj STUDENI -> System.out.println (30); zadani -> System.out.println (31); }

Imajte na umu da pauza ključna riječ nije potrebna, a štoviše, ovdje je ne možemo koristiti. Automatski se podrazumijeva da je svaki meč različit i prolazak nije opcija. Umjesto toga, možemo nastaviti koristiti stariji stil kada nam zatreba.

Desna strana strelice mora biti izraz, blok ili izjava o bacanju. Sve drugo je pogreška. Ovo također rješava problem definiranja varijabli unutar naredbi prekidača - što se može dogoditi samo unutar bloka, što znači da se automatski dosežu do tog bloka:

prekidač (mjesec) {case VELJAČA -> {int dana = 28; } slučaj APRIL -> {int dana = 30; } ....}

U starijoj naredbi prebacivanja stila ovo bi bila pogreška zbog duplicirane varijable dana. Zahtjev za upotrebom bloka to izbjegava.

Lijeva strana strelice može biti bilo koji broj vrijednosti odvojenih zarezom. To omogućava neke iste funkcije kao i prolazak, ali samo za cijelu utakmicu i nikada slučajno:

prekidač (mjesec) {case VELJAČA -> System.out.println (28); slučaj TRAVANJ, LIPANJ, RUJAN, STUDENI -> System.out.println (30); zadani -> System.out.println (31); }

Zasad je sve ovo moguće s trenutnim načinom na koji sklopka izjave djeluju i čine ga urednijim. Međutim, ovo ažuriranje također donosi mogućnost korištenja a sklopka iskaz kao izraz. Ovo je značajna promjena za Javu, ali je u skladu s tim koliko drugih jezika - uključujući i druge JVM jezike - počinje raditi.

To omogućuje sklopka izraz da se razriješi na vrijednost, a zatim da se ta vrijednost koristi u drugim izrazima - na primjer, zadatak:

završni var dani = prekidač (mjesec) {slučaj VELJAČA -> 28; slučaj TRAVANJ, LIPANJ, RUJAN, STUDENI -> 30; zadano -> 31; }

Ovdje koristimo sklopka izraz za generiranje broja, a zatim taj broj dodjeljujemo izravno varijabli.

Prije je to bilo moguće samo definiranjem varijable dana kao null a zatim mu dodijeli vrijednost unutar sklopka slučajevi. To je značilo dana ne može biti konačna i potencijalno može biti neraspoređena ako propustimo slučaj.

4. Nadolazeće promjene

Do sada su sve ove promjene ili već dostupne ili će biti u nadolazećem izdanju. Postoje neke predložene izmjene u sklopu Projekta Amber koje još nisu predviđene za objavljivanje.

4.1. Literatura sirovih žica

Trenutno Java ima točno jedan način da definira String literal - okružujući sadržaj dvostrukim navodnicima. Ovo je jednostavno za upotrebu, ali u složenijim slučajevima pati od problema.

Posebno, teško je pisati nizove koji sadrže određene znakove - uključujući, ali ne ograničavajući se na: nove retke, dvostruke navodnike i znakove s kosom crtom. To može biti posebno problematično u putovima datoteka i regularnim izrazima gdje ti znakovi mogu biti češći nego što je tipično.

JEP-326 predstavlja novi String literal tip nazvan Raw String Literals. Oni su zatvoreni u oznake za povratne oznake umjesto dvostrukih navodnika i mogu sadržavati bilo koje znakove unutar njih.

To znači da postaje moguće pisati nizove koji obuhvaćaju više redaka, kao i nizove koji sadrže navodnike ili obrnute kose crte, a da ih ne treba izbjeći. Tako postaju lakši za čitanje.

Na primjer:

// Staza datotečnog sustava "C: \ Dev \ file.txt" `C: \ Dev \ file.txt` // Redovni izraz" \ d + \. \ d \ d "` \ d + \. \ d \ d` // Višeredni "Hello \ nWorld" `Hello World`

U sva tri slučaja, lakše je vidjeti što se događa u verziji sa zaokretima, koja je također puno manje podložna pogreškama.

Novi Raw String Literals također nam omogućuju da bez komplikacija uvrstimo i same povratne znakove. Broj povratnih znakova upotrijebljenih za početak i kraj niza može biti koliko god želite - to ne mora biti samo jedan povratni udarac. Niz završava tek kad dosegnemo jednaku duljinu povratnih znakova. Tako, na primjer:

`` Ovaj niz dopušta jedan "" "jer je umotan u dva povratna znaka``

Oni nam omogućuju da upisujemo nizove točno onakve kakvi jesu, umjesto da ikad trebaju posebne sekvence da bi neki likovi funkcionirali.

4.2. Lambda ostaci

JEP-302 uvodi neka mala poboljšanja u načinu rada lambda.

Glavne promjene su u načinu na koji se obrađuju parametri. Prvo, ova promjena uvodi mogućnost korištenja donje crte za neiskorišteni parametar tako da ne generiramo imena koja nisu potrebna. To je bilo moguće ranije, ali samo za jedan parametar, jer je podvlaka bilo valjano ime.

Java 8 uvela je promjenu tako da upozorenje koristi podvlaka kao naziv. Java 9 je zatim to napredovala da bi umjesto toga postala pogreška, sprječavajući nas da ih uopće koristimo. Ova nadolazeća promjena omogućuje im lambda parametre bez izazivanja sukoba. To bi omogućilo, na primjer, sljedeći kod:

jdbcTemplate.queryForObject ("SELECT * FROM users WHERE user_id = 1", (rs, _) -> parseUser (rs))

Pod ovim poboljšanjem, definirali smo lambdu s dva parametra, ali samo je prvi vezan za ime. Drugi nije dostupan, ali jednako tako, napisali smo ga na ovaj način jer ga nemamo potrebe koristiti.

Druga velika promjena u ovom poboljšanju je omogućiti lambda parametre da zasjene imena iz trenutnog konteksta. To trenutno nije dopušteno, što može dovesti do toga da napišemo neki manje od idealnog koda. Na primjer:

Ključ niza = computeSomeKey (); map.computeIfAbsent (key, key2 -> key2.length ());

Nema stvarne potrebe, osim prevoditelja, zašto ključ i tipka2 ne mogu dijeliti ime. Lambda nikad ne treba referencirati varijablu ključ, a prisiljavanje nas na to čini kod ružnijim.

Umjesto toga, ovo poboljšanje omogućuje nam da ga napišemo na očitiji i jednostavniji način:

Ključ niza = computeSomeKey (); map.computeIfAbsent (key, key -> key.length ());

Dodatno, postoji predložena promjena u ovom poboljšanju koja bi mogla utjecati na razlučivost preopterećenja kada preopterećena metoda ima lambda argument. Trenutno postoje slučajevi u kojima to može dovesti do dvosmislenosti zbog pravila prema kojima djeluje rješavanje preopterećenja, a ovaj JEP može ta pravila malo prilagoditi kako bi se izbjegle neke od tih nejasnoća.

Na primjer, trenutno sastavljač smatra da su sljedeće metode dvosmislene:

m (predikat ps) {...} m (funkcija fss) {...}

Obje ove metode uzimaju lambdu koja ima jedan Niz parametar i ima nevažeći tip povrata. Programeru je očito da su različiti - jedan vraća a Niz, a drugi, a boolean, ali sastavljač će ih tretirati kao dvosmislene.

Ovaj JEP može otkloniti ovaj nedostatak i dopustiti da se ovo preopterećenje izričito tretira.

4.3. Usklađivanje uzoraka

JEP-305 uvodi poboljšanja u načinu na koji možemo raditi s instanceof prisila rukovaoca i automatskog tipa.

Trenutno, kada uspoređujemo tipove u Javi, moramo koristiti instanceof Operator da vidi je li vrijednost ispravnog tipa, a zatim nakon toga trebamo prebaciti vrijednost na ispravan tip:

if (obj instanceof String) {String s = (String) obj; // koristimo s}

To djeluje i odmah se shvaća, ali je složenije nego što je potrebno. Imamo vrlo očigledno ponavljanje u našem kodu, a time i rizik od dopuštanja uvlačenja pogrešaka.

Ovo poboljšanje vrši sličnu prilagodbu kao instanceof kao što je prethodno napravljeno pod pokušajte s resursima u Javi 7. Ovom promjenom deklaracija usporedbe, emitiranja i varijable umjesto toga postaju jedan izraz:

if (obj instanceof String s) {// koristi s}

To nam daje jednu izjavu, bez dupliciranja i rizika od uvlačenja pogrešaka, a opet izvodi isto kao gore.

Ovo će također ispravno raditi na svim granama, omogućujući sljedećem:

if (obj instanceof String s) {// ovdje može koristiti s} else {// ovdje ne može koristiti s}

Poboljšanje će također ispravno raditi na različitim granicama opsega, prema potrebi. Varijabla koju je deklarirao instanceof klauzula ispravno će zasjeniti varijable definirane izvan nje, kako se očekivalo. To će se dogoditi samo u odgovarajućem bloku:

Niz s = "Pozdrav"; if (obj instanceof String s) {// s odnosi se na obj} else {// s odnosi se na varijablu definiranu prije izraza if}

Ovo također djeluje unutar istog ako klauzula, na isti način na koji se oslanjamo na za null provjere:

if (obj instanceof String s && s.length ()> 5) {// s je Niz duži od 5 znakova}

Trenutno je to planirano samo za ako izjave, ali budući će ga posao vjerojatno proširiti i na rad s njim prebaciti izraze također.

4.4. Sažeta tijela tijela

JEP nacrt 8209434 je prijedlog za podršku pojednostavljenim definicijama metoda, na način sličan onome kako funkcioniraju lambda definicije.

Trenutno Lambdu možemo definirati na tri različita načina: s tijelom, kao jedan izraz ili kao referenca metode:

ToIntFunction lenFn = (String s) -> {return s.length (); }; ToIntFunction lenFn = (Nizovi s) -> s.dužina (); ToIntFunction lenFn = Niz :: duljina;

Međutim, što se tiče pisanja stvarnih tijela metode klase, trenutno ih moramo u potpunosti zapisati.

Ovim prijedlogom podržavaju se i izrazi i obrasci reference za ove metode, u slučajevima kada su primjenjivi. To će pomoći da pojedine metode budu puno jednostavnije nego što su trenutno.

Na primjer, getter metoda ne treba puno tijelo metode, ali se može zamijeniti jednim izrazom:

Niz getName () -> ime;

Jednako tako, metode koje su jednostavno omotači oko drugih metoda možemo zamijeniti pozivom reference metode, uključujući prosljeđivanje parametara:

int length (String s) = Niz :: duljina

To će omogućiti jednostavnije metode u slučajevima kada imaju smisla, što znači da će biti manje vjerojatno da će prikriti stvarnu poslovnu logiku u ostatku razreda.

Imajte na umu da je ovo još uvijek u statusu skice i kao takvo podložno je značajnim promjenama prije isporuke.

5. Pojačani nabroji

JEP-301 prethodno je trebao biti dio Projekta Amber. To bi donijelo neka poboljšanja popisa, izričito dopuštajući da pojedini elementi nabrajanja imaju različite informacije o generičkom tipu.

Na primjer, to bi omogućilo:

enum Primitivno {INT (Integer.class, 0) {int mod (int x, int y) {return x% y; } int dodaj (int x, int y) {return x + y; }}, FLOAT (Float.class, 0f) {long add (long x, long y) {return x + y; }}, ...; završna klasa boxClass; konačni X defaultValue; Primitivno (Class boxClass, X defaultValue) {this.boxClass = boxClass; this.defaultValue = defaultValue; }}

Nažalost, eksperimenti ovog poboljšanja unutar aplikacije Java kompajlera dokazali su da je manje održiv nego što se prije mislilo. Dodavanje podataka o generičkom tipu elementima nabrajanja onemogućilo je upotrebu tih nabrajanja kao generičkih tipova na drugim klasama - na primjer, EnumSet. To drastično smanjuje korisnost poboljšanja.

Kao takav, ovo je poboljšanje trenutno na čekanju dok se ne mogu razraditi ovi detalji.

6. Sažetak

Ovdje smo pokrili mnogo različitih značajki. Neki od njih već su dostupni, drugi će uskoro biti dostupni, a još ih je planirano za buduća izdanja. Kako ovi mogu poboljšati vaše sadašnje i buduće projekte?


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