Úvod do projektu Amber

1. Čo je Project Amber

Project Amber je aktuálna iniciatíva vývojárov Java a OpenJDK, ktorej cieľom je priniesť do JDK niekoľko malých, ale zásadných zmien, ktoré spríjemnia vývojový proces. Tento proces trvá od roku 2017 a už priniesol určité zmeny v prostredí Java 10 a 11, ďalšie sú naplánované na začlenenie do prostredia Java 12 a ďalšie prídu v budúcich vydaniach.

Všetky tieto aktualizácie sú zabalené vo forme JEP - schéma návrhu vylepšenia JDK.

2. Doručené aktualizácie

Do dnešného dňa Project Amber úspešne priniesol niektoré zmeny do aktuálne vydaných verzií JDK - JEP-286 a JEP-323.

2.1. Odvodenie typu lokálnej premennej

Java 7 predstavila operátora Diamond Operator ako spôsob uľahčenia práce s generikami. Táto vlastnosť znamená, že pri definovaní premenných už nemusíme písať všeobecné informácie do toho istého príkazu viackrát:

Zoznam reťazcov = new ArrayList (); // Zoznamy reťazcov Java 6 = new ArrayList (); // Java 7

Java 10 zahŕňala dokončenú prácu na JEP-286, čo umožňuje nášmu kódu Java definovať lokálne premenné bez potreby duplikovania informácií o type všade, kde už ich kompilátor má k dispozícii. V širšej komunite sa to označuje ako var kľúčové slovo a prináša podobné funkcie ako Java, ktorá je k dispozícii v mnohých iných jazykoch.

S touto prácou kedykoľvek definujeme lokálnu premennú, môžeme použiť var namiesto definície úplného typua kompilátor automaticky vypracuje správne informácie o type, ktoré sa majú použiť:

var strings = new ArrayList ();

Vo vyššie uvedenom, premenná struny je určený ako typ ArrayList (), ale bez potreby duplikovania informácií na rovnakom riadku.

Môžeme to použiť kdekoľvek, kde použijeme lokálne premenné, bez ohľadu na to, ako je hodnota určená. Patria sem návratové typy a výrazy, ako aj jednoduché priradenia, ako je uvedené vyššie.

Slovo var je špeciálny prípad, pretože to nie je vyhradené slovo. Namiesto toho je to názov špeciálneho typu. To znamená, že je možné použiť toto slovo pre ďalšie časti kódu - vrátane názvov premenných. Dôrazne sa to odporúča nerobiť, aby nedošlo k zámene.

Odvodenie lokálneho typu môžeme použiť, iba ak ako súčasť deklarácie poskytneme skutočný typ. Je zámerne navrhnutý tak, aby nefungoval, ak je jeho hodnota výslovne uvedená nulový, keď nie je poskytnutá vôbec žiadna hodnota alebo keď poskytnutá hodnota nemôže určiť presný typ - napríklad definícia lambda:

var neznámyTyp; // Žiadna hodnota poskytnutá na odvodenie typu z var nullType = null; // Zadaná explicitná hodnota, ale má hodnotu null var lambdaType = () -> System.out.println ("Lambda"); // Lambda bez definovania rozhrania

Avšak hodnota môže byť nulový ak je to návratová hodnota z iného hovoru pretože samotný hovor poskytuje informácie o type:

Voliteľný názov = Optional.empty (); var nullName = name.orElse (null);

V tomto prípade, nullName odvodí typ String pretože to je návratový typ name.orElse () je.

Takto definované premenné môžu mať akékoľvek iné modifikátory rovnakým spôsobom ako ktorákoľvek iná premenná - napríklad, tranzitívne, synchronizované, a konečné.

2.2. Odvodenie lokálnej premennej typu pre lambdy

Vyššie uvedená práca nám umožňuje deklarovať lokálne premenné bez potreby duplikovania informácií o type. To však nefunguje na zoznamoch parametrov, najmä na parametroch funkcií lambda, čo sa môže zdať prekvapujúce.

V prostredí Java 10 môžeme definovať funkcie Lambda jedným z dvoch spôsobov - buď výslovným vyhlásením typov, alebo ich úplným vynechaním:

names.stream () .filter (Názov reťazca -> name.length ()> 5). mapa (name -> name.toUpperCase ());

Tu má druhý riadok výslovnú deklaráciu typu - String - keďže tretí riadok to úplne vynecháva a prekladač zistí správny typ. Čo nemôžeme urobiť, je použiť var píšte sem.

Java 11 to umožňuje, takže môžeme namiesto toho napísať:

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

To je potom v súlade s používaním var zadajte inde v našom kóde.

Lambdas nás vždy obmedzoval na používanie úplných mien typov buď pre každý parameter, alebo pre žiadny z nich. To sa nezmenilo, a použitie var musí byť pre každý parameter alebo pre žiadny z nich:

numbers.stream () .reduce (0, (var a, var b) -> a + b); // Platné cisla.stream () .reduce (0, (var a, b) -> a + b); // Neplatné cisla.stream () .reduce (0, (var a, int b) -> a + b); // Neplatné

Tu je prvý príklad úplne platný - pretože obidva parametre lambda používajú oba var. Druhá a tretia sú však nelegálne, pretože sa používa iba jeden parameter var, aj keď v treťom prípade máme tiež explicitný názov typu.

3. Bezprostredné aktualizácie

Okrem aktualizácií, ktoré sú už k dispozícii vo vydaných súboroch JDK, obsahuje nadchádzajúce vydanie JDK 12 jednu aktualizáciu - JEP-325.

3.1. Prepnúť výrazy

JEP-325 prináša podporu pre zjednodušenie tohto spôsobu prepínač vyhlásenia fungujú a umožňujú ich použitie ako výrazov ešte viac zjednodušiť kód, ktorý ich využíva.

V súčasnosti je prepínač vyhlásenie funguje veľmi podobne ako v jazykoch ako C alebo C ++. Vďaka týmto zmenám je oveľa podobnejšia ako v prípade kedy vyhlásenie vo veci Kotlin alebo zápas vyhlásenie v Scale.

Vďaka týmto zmenám syntax pre definíciu príkazu switch vyzerá podobne ako syntax lambdas, s použitím -> symbol. Toto sa nachádza medzi zhodou prípadov a kódom, ktorý sa má vykonať:

switch (mesiac) {case FEBRUARY -> System.out.println (28); prípad APRÍL -> System.out.println (30); prípad JÚN -> System.out.println (30); prípad SEPTEMBER -> System.out.println (30); prípad LISTOPAD -> System.out.println (30); predvolené -> System.out.println (31); }

Všimnite si, že prestávka kľúčové slovo nie je potrebné, a čo viac, tu ho nemôžeme použiť. Automaticky sa predpokladá, že každý zápas je zreteľný a postup nie je možný. Namiesto toho môžeme v prípade potreby naďalej používať starší štýl.

Pravá strana šípky musí byť buď výraz, blok alebo vyhlásenie vrhov. Čokoľvek iné je chyba. Týmto sa tiež vyrieši problém definovania premenných vo vnútri príkazov switch - čo sa môže stať iba vo vnútri bloku, čo znamená, že sú automaticky priradené k danému bloku:

switch (month) {case FEBRUARY -> {int days = 28; } prípad APRÍL -> {int dni = 30; } ....}

V staršom príkaze na prepínanie štýlov by to bola chyba z dôvodu duplicitnej premennej dni. Tomu sa vyhýba požiadavka na použitie bloku.

Na ľavej strane šípky môže byť ľubovoľný počet hodnôt oddelených čiarkou. To umožňuje niektoré z rovnakých funkcií ako prielom, ale iba na celý zápas a nikdy nie náhodne:

switch (month) {case FEBRUARY -> System.out.println (28); prípad APRÍL, JÚN, SEPTEMBER, LISTOPAD -> System.out.println (30); predvolené -> System.out.println (31); }

Zatiaľ je toto všetko možné súčasným spôsobom prepínač vyhlásenia fungujú a robia ich poriadok. Avšak táto aktualizácia prináša aj schopnosť používať a prepínač výrok ako výraz. Pre Javu je to významná zmena, ale je to v súlade s tým, koľko ďalších jazykov - vrátane iných jazykov JVM - začína pracovať.

To umožňuje prepínač výraz vyriešiť na hodnotu a potom použiť túto hodnotu v iných príkazoch - napríklad zadanie:

final var days = switch (month) {case FEBRUARY -> 28; prípad APRÍL, JÚN, SEPTEMBER, LISTOPAD -> 30; predvolené -> 31; }

Tu používame a prepínač výraz na vygenerovanie čísla a potom toto číslo priradíme priamo k premennej.

Predtým to bolo možné iba definovaním premennej dni ako nulový a potom mu priradiť hodnotu vo vnútri prepínač prípadoch. To znamenalo, že dni nemôže byť konečné a môže byť potenciálne nepriradené, ak by sme premeškali prípad.

4. Pripravované zmeny

Zatiaľ sú všetky tieto zmeny buď už k dispozícii, alebo budú v nadchádzajúcom vydaní. Súčasťou projektu Amber sú niektoré navrhované zmeny, ktoré zatiaľ nie sú naplánované na vydanie.

4.1. Surové reťazcové literály

V súčasnosti má Java presne jeden spôsob, ako definovať reťazcový literál - obklopením obsahu dvojitými úvodzovkami. Toto sa dá ľahko používať, v komplikovanejších prípadoch však trpí problémami.

Konkrétne je ťažké napísať reťazce, ktoré obsahujú určité znaky - okrem iného: nové riadky, dvojité úvodzovky a znaky spätného lomítka. To môže byť obzvlášť problematické pri cestách k súborom a regulárnych výrazoch, kde môžu byť tieto znaky bežnejšie ako je bežné.

JEP-326 predstavuje nový reťazcový literálny typ s názvom Raw String Literals. Sú uzavreté v spätných značkách namiesto dvojitých úvodzoviek a môžu obsahovať akékoľvek znaky vo vnútri nich.

To znamená, že je možné písať reťazce, ktoré obsahujú viac riadkov, ako aj reťazce, ktoré obsahujú úvodzovky alebo spätné lomky, bez toho, aby ste museli uniknúť. Stávajú sa tak ľahšie čitateľnými.

Napríklad:

// Cesta k súborovému systému "C: \ Dev \ file.txt" "C: \ Dev \ file.txt` // Regex" \ d + \. \ d \ d "" \ d + \. \ d \ d` // Viacriadkové "Hello \ nWorld" "Hello World`

Vo všetkých troch prípadoch je ľahšie zistiť, čo sa deje vo verzii so zadnými lístkami, ktoré sú tiež oveľa menej náchylné na chyby.

Nové súbory Raw String Literals nám tiež umožňujú bez komplikácií zahrnúť aj samotné backticks. Počet spätných väzieb použitých na začatie a ukončenie reťazca môže byť ľubovoľný, môže to byť iba jeden spätný väzok. Šnúrka končí, až keď dosiahneme rovnakú dĺžku backtickov. Napríklad:

„Tento reťazec umožňuje jeden znak„ “, pretože je zabalený v dvoch spätných kliknutiach.“

Umožňujú nám písať reťazce presne také, aké sú, skôr ako by sme niekedy potrebovali špeciálne sekvencie, aby určité znaky fungovali.

4.2. Zvyšky lambdy

JEP-302 zavádza niektoré malé vylepšenia spôsobu práce lambdas.

Hlavné zmeny sa týkajú spôsobu zaobchádzania s parametrami. Po prvé, táto zmena zavádza možnosť použiť podčiarknutie pre nepoužitý parameter, aby sme negenerovali názvy, ktoré nie sú potrebné. To bolo predtým možné, ale iba pre jeden parameter, pretože podčiarkovník bol platný názov.

Java 8 zaviedla zmenu, takže použitie podčiarkovníka ako názvu je varovaním. Java 9 potom postupovala tak, že sa namiesto nej stala chyba, a zabránila nám tak ich vôbec používať. Táto pripravovaná zmena im umožňuje parametre lambda bez toho, aby spôsobovala akékoľvek konflikty. To by umožnilo napríklad nasledujúci kód:

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

V rámci tohto vylepšenia definovali sme lambdu s dvoma parametrami, ale iba prvý je viazaný na meno. Druhá nie je prístupná, ale rovnako sme ju napísali týmto spôsobom, pretože ju nepotrebujeme používať.

Druhou významnou zmenou v tomto vylepšení je umožniť parametrom lambda tieňovať názvy z aktuálneho kontextu. Toto momentálne nie je povolené, čo môže spôsobiť, že napíšeme menej ako ideálny kód. Napríklad:

Reťazcový kľúč = computeSomeKey (); map.computeIfAbsent (kľúč, kľúč2 -> kľúč2.dĺžka ());

Nie je potrebné, okrem kompilátora, prečo kľúč a key2 nemôže zdieľať meno. Lambda nikdy nemusí odkazovať na premennú kľúč, a nútenie nás k tomu robí kód škaredším.

Namiesto toho nám toto vylepšenie umožňuje napísať ho zjavnejším a jednoduchším spôsobom:

Reťazcový kľúč = computeSomeKey (); map.computeIfAbsent (kľúč, kľúč -> key.length ());

Navyše, v tomto vylepšení je navrhovaná zmena, ktorá by mohla ovplyvniť rozlíšenie preťaženia, keď má preťažená metóda lambda argument. V súčasnosti existujú prípady, keď to môže viesť k nejasnostiam v dôsledku pravidiel, podľa ktorých funguje riešenie preťaženia, a tento JEP môže tieto pravidlá mierne upraviť, aby sa niektorým z týchto nejasností vyhnul.

Napríklad, v súčasnosti kompilátor považuje nasledujúce metódy za nejednoznačné:

m (Predikát ps) {...} m (Funkcia fss) {...}

Obe tieto metódy používajú lambdu, ktorá má jediný String parameter a má neplatný návratový typ. Vývojárovi je zrejmé, že sú odlišné - vráti sa a Stringa druhá, a boolovský, ale kompilátor ich bude považovať za nejednoznačné.

Tento JEP môže vyriešiť tento nedostatok a umožniť výslovné riešenie tohto preťaženia.

4.3. Zhoda vzorov

JEP-305 predstavuje vylepšenia spôsobu, akým môžeme pracovať s inštancia operátor a automatické vynútenie typu.

V súčasnosti musíme pri porovnávaní typov v Jave používať inštancia operátor, aby sme zistili, či je hodnota správneho typu, a potom musíme hodnotu odovzdať do správneho typu:

if (obj instanceof String) {String s = (String) obj; // použitie s}

To funguje a je to okamžite pochopené, ale je to komplikovanejšie, ako je potrebné. V našom kódexe sa nachádza veľmi zrejmé opakovanie, a preto existuje riziko, že sa do neho vkradnú chyby.

Toto vylepšenie robí podobnú úpravu ako inštancia operátor, ako bolo predtým vyrobené pod vyskúšajte zdroje v prostredí Java 7. S touto zmenou sa porovnanie, obsadenie a deklarácia premennej stanú jediným vyhlásením:

if (obj instanceof String s) {// použitie s}

Toto nám dáva jediné vyhlásenie bez duplikácie a rizika vniknutia chýb, a napriek tomu vykonáva to isté ako vyššie uvedené.

Bude to tiež fungovať správne vo všetkých pobočkách, čo umožní fungovanie nasledujúcich produktov:

if (obj instanceof String s) {// tu môže použiť s}} inde {// tu nemôže byť s}

Vylepšenie bude tiež podľa potreby fungovať správne v rôznych hraniciach rozsahu. Premenná deklarovaná inštancia klauzula bude podľa očakávania správne tieňovať premenné definované mimo nej. Stane sa to iba v príslušnom bloku, aj keď:

Reťazec s = "Dobrý deň"; if (obj instanceof String s) {// s odkazuje na obj} else {// s odkazuje na premennú definovanú pred príkazom if}

To funguje aj v rámci toho istého ak doložkarovnakým spôsobom, na aký sa spoliehame nulový kontroly:

if (obj instanceof String s && s.length ()> 5) {// s je reťazec s viac ako 5 znakmi}

V súčasnosti je to plánované iba na ak Vyhlásenia, ale budúca práca to pravdepodobne rozšíri tak, aby fungovala prepnúť výrazy tiež.

4.4. Orgány so stručnou metódou

Návrh JEP 8209434 je návrh na podporu zjednodušených definícií metódpodobným spôsobom, ako fungujú definície lambda.

Momentálne môžeme lambdu definovať tromi rôznymi spôsobmi: s telom, ako jediný výraz alebo ako odkaz na metódu:

ToIntFunction lenFn = (String s) -> {return s.length (); }; ToIntFunction lenFn = (String s) -> s.length (); ToIntFunction lenFn = String :: length;

Avšak pokiaľ ide o písanie skutočných telies metód triedy, musíme ich momentálne vypísať celé.

Tento návrh má tiež podporiť formuláre výrazov a referenčných metód pre tieto metódy, v prípadoch, keď sú uplatniteľné. To pomôže zachovať určité metódy oveľa jednoduchšie, ako sú v súčasnosti.

Napríklad metóda getter nepotrebuje celé telo metódy, ale je možné ju nahradiť jediným výrazom:

Reťazec getName () -> meno;

Rovnako môžeme nahradiť metódy, ktoré sú jednoduchým obalom okolo iných metód, volaním referenčnej metódy, vrátane odovzdávania parametrov naprieč:

int length (String s) = String :: length

Umožní to jednoduchšie metódy v prípadoch, keď to bude mať zmysel, čo znamená, že bude menej pravdepodobné, že zakryjú skutočnú obchodnú logiku vo zvyšku triedy.

Upozorňujeme, že tento stav je stále v koncepte a ako taký sa pred doručením významne zmení.

5. Enhanced Enums

JEP-301 bol predtým naplánovaný ako súčasť projektu Amber. To by prinieslo niekoľko vylepšení enumov, výslovne umožňujúcich jednotlivým prvkom enum mať odlišné informácie o všeobecnom type.

Umožnilo by to napríklad:

enum Primitive {INT (Integer.class, 0) {int mod (int x, int y) {return x% y; } int add (int x, int y) {return x + y; }}, FLOAT (Float.class, 0f) {long add (long x, long y) {return x + y; }}, ...; konečná trieda boxClass; konečná X defaultValue; Primitive (Class boxClass, X defaultValue) {this.boxClass = boxClass; this.defaultValue = defaultValue; }}

Bohužiaľ experimenty tohto vylepšenia v aplikácii kompilátora Java dokázali, že je menej životaschopné, ako sa doteraz myslelo. Vďaka pridaniu informácií o všeobecnom type k prvkom výčtu bolo nemožné potom tieto výčty použiť ako všeobecné typy v iných triedach - napríklad EnumSet. To drasticky znižuje užitočnosť vylepšenia.

Ako taký, toto vylepšenie je momentálne pozastavené, kým nebude možné vypracovať tieto podrobnosti.

6. Zhrnutie

Pokryli sme tu veľa rôznych funkcií. Niektoré z nich sú už k dispozícii, iné budú čoskoro k dispozícii a pre ďalšie vydania sa plánujú ďalšie. Ako môžu tieto vylepšiť vaše súčasné a budúce projekty?


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