Bevezetés a Borostyán projektbe

1. Mi a projekt Amber

A Project Amber a Java és az OpenJDK fejlesztőinek jelenlegi kezdeményezése, amelynek célja néhány apró, de lényeges változás végrehajtása a JDK-ban, hogy a fejlesztési folyamat szebb legyen. Ez 2017 óta tart, és már végrehajtott néhány változást a Java 10-es és 11-es verziókban, másokat a Java 12-be történő beépítésre terveztek, és még többet fognak kapni a jövőbeni kiadások.

Ezek a frissítések mind JEP-k formájában vannak csomagolva - a JDK Enhancement javaslati sémája.

2. Frissített frissítések

A mai napig a Project Amber sikeresen végrehajtott néhány módosítást a JDK jelenleg kiadott verzióiban - JEP-286 és JEP-323.

2.1. Helyi változó típusú következtetés

A Java 7 bemutatta a Gyémánt Operátort, hogy megkönnyítse a generikus gyógyszerekkel való munkát. Ez a funkció azt jelenti, hogy a változók definiálásakor már nem kell általános információkat többször írnunk ugyanabban az utasításban:

Lista karakterláncok = új ArrayList (); // Java 6 Lista karakterláncok = new ArrayList (); // Java 7

A Java 10 tartalmazta a JEP-286 elkészült munkáját, lehetővé téve a Java kódunk számára a helyi változók definiálását anélkül, hogy meg kellene ismételni a típusinformációkat, ahol a fordító már elérhető. Ezt a tágabb közösségben a var kulcsszó, és hasonló funkcionalitást hoz a Java-hoz, mint ami sok más nyelven elérhető.

Ezzel a munkával amikor definiálunk egy helyi változót, használhatjuk a var kulcsszó a teljes típusdefiníció helyett, és a fordító automatikusan kidolgozza a megfelelő típusú információkat:

var stringek = new ArrayList ();

A fentiekben a változó húrok típusúnak minősül Tömb lista(), de anélkül, hogy meg kellene másolni az információkat ugyanazon a vonalon.

Ezt bárhol használhatjuk, ahol helyi változókat használunk, függetlenül az érték meghatározásának módjától. Ez magában foglalja a visszatérési típusokat és kifejezéseket, valamint az egyszerű hozzárendeléseket, mint a fentiek.

A szó var egy különleges eset, mivel nem fenntartott szó. Ehelyett egy speciális típusú név. Ez azt jelenti, hogy a szó a kód más részeire is használható, beleértve a változó neveket is. Erősen ajánlott, hogy ezt ne tegye a félreértések elkerülése érdekében.

Csak akkor használhatunk helyi típusú következtetést, ha a deklaráció részeként tényleges típust adunk meg. Szándékosan úgy tervezték, hogy ne működjön, ha az érték kifejezetten szerepel nulla, amikor egyáltalán nem ad meg értéket, vagy ha a megadott érték nem tudja meghatározni a pontos típust - például egy Lambda definíciót:

var unknownType; // Nincs megadva a következtetési típushoz a var nullType = null érték; // explicit érték megadva, de ez null var lambdaType = () -> System.out.println ("Lambda"); // Lambda az interfész meghatározása nélkül

Azonban, az érték lehet nulla ha valamilyen más hívás visszatérési értéke mivel maga a hívás típusinformációt nyújt:

Opcionális név = Opcionális.empty (); var nullNév = név.vagyElse (null);

Ebben az esetben, nullName következtetni fog a típusra Húr mert ez a visszatérési típus név.vagyElse () van.

Az így definiált változók bármilyen más módosítóval rendelkezhetnek ugyanúgy, mint bármely más változó - például, tranzitív, szinkronizált, és végső.

2.2. Helyi változó típusú következtetés a Lambdas esetében

A fenti munka lehetővé teszi számunkra a helyi változók deklarálását anélkül, hogy meg kellene ismételnünk a típusú információkat. Ez azonban nem működik a paraméterlistákon, és különösen a lambda függvények paraméterein, amelyek meglepőnek tűnhetnek.

A Java 10-ben a Lambda függvényeket kétféleképpen definiálhatjuk - a típusok kifejezett deklarálásával vagy azok teljes kihagyásával:

nevek.stream () .szűrő (Karakterlánc neve -> név.hossz ()> 5) .térkép (név -> név.azUpperCase ());

Itt a második sorban kifejezett típusú deklaráció van - Húr - míg a harmadik sor teljesen kihagyja, és a fordító kidolgozza a helyes típust. Amit nem tehetünk meg, az a var írj ide.

A Java 11 lehetővé teszi ennek megvalósulását, így ehelyett írhatunk:

nevek.stream (). szűrő (var név -> név.hossz ()> 5) .térkép (var név -> név.toUpperCase ());

Ez akkor összhangban van a var írja be másutt a kódunkba.

A lambdák mindig arra korlátoztak minket, hogy teljes típusneveket használjunk minden paraméterhez, vagy egyikhez sem. Ez nem változott, és a ... haszna var vagy minden paraméterre, vagy egyikre sem:

számok.folyam () .csökkentse (0, (var a, var b) -> a + b); // Érvényes számok.stream () .reduce (0, (var a, b) -> a + b); // Érvénytelen számok.stream () .reduce (0, (var a, int b) -> a + b); // Érvénytelen

Itt az első példa tökéletesen érvényes - mert a két lambda paraméter mindkettőt használja var. A második és a harmadik azonban törvénytelen, mert csak egy paraméter használ var, annak ellenére, hogy a harmadik esetben van egy kifejezett típusnévünk is.

3. Közelgő frissítések

A már kiadott JDK-kban elérhető frissítések mellett a következő JDK 12 kiadás tartalmaz egy frissítést - JEP-325.

3.1. Váltás a kifejezések között

A JEP-325 támogatja ennek egyszerűsítését kapcsoló az állítások működnek, és lehetővé teszik, hogy kifejezésekként használják őket hogy még jobban leegyszerűsítsük az őket használó kódot.

Jelenleg a kapcsoló utasítás nagyon hasonló módon működik, mint az olyan nyelveken, mint a C vagy a C ++. Ezek a változások sokkal hasonlóbbá teszik a mikor nyilatkozat Kotlinban vagy a mérkőzés nyilatkozat a Scalában.

Ezekkel a változásokkal a switch utasítás definiálásának szintaxisa hasonló a lambdaséhoz, a -> szimbólum. Ez az esetegyezés és a végrehajtandó kód között helyezkedik el:

váltás (hónap) {eset FEBRUÁR -> System.out.println (28); eset ÁPRILIS -> System.out.println (30); eset JÚNIUS -> System.out.println (30); eset SZEPTEMBER -> System.out.println (30); NOVEMBER eset -> System.out.println (30); alapértelmezett -> System.out.println (31); }

Vegye figyelembe, hogy a szünet kulcsszóra nincs szükség, és mi több, itt nem tudjuk használni. Automatikusan arra utal, hogy minden meccs különálló, és az átütés nem lehetséges. Ehelyett továbbra is használhatjuk a régebbi stílust, amikor arra szükségünk van.

A nyíl jobb oldalán kifejezésnek, blokknak vagy dobó utasításnak kell lennie. Minden más hiba. Ez megoldja azt a problémát is, hogy meghatározzuk a kapcsolók utasításainak belsejében található változókat - amelyek csak egy blokkon belül történhetnek meg, ami azt jelenti, hogy automatikusan az adott blokkba kerülnek:

váltás (hónap) {eset FEBRUÁR -> {int nap = 28; } eset ÁPRILIS -> {int nap = 30; } ....}

A régebbi stíluskapcsoló utasításban ez hiba lenne a változó duplikálása miatt napok. A blokk használatának követelménye ezt elkerüli.

A nyíl bal oldala tetszőleges számú, vesszővel elválasztott érték lehet. Ennek célja, hogy ugyanolyan funkcionalitást engedélyezzen, mint az áttörés, de csak a mérkőzés teljes egészében és soha nem véletlenül:

váltás (hónap) {eset FEBRUÁR -> System.out.println (28); ügy: ÁPRILIS, JÚNIUS, SZEPTEMBER, NOVEMBER -> System.out.println (30); alapértelmezett -> System.out.println (31); }

Eddig mindez a jelenlegi módon lehetséges kapcsoló az állítások működnek és rendezettebbé teszik. Azonban, ez a frissítés az a használatát is lehetővé teszi kapcsoló állítás mint kifejezés. Ez jelentős változás a Java számára, de összhangban van azzal, hogy hány más nyelv - beleértve a többi JVM-nyelvet is - kezd működni.

Ez lehetővé teszi a kapcsoló kifejezést, hogy feloldódjon egy értékre, majd felhasználja ezt az értéket más utasításokban - például egy megbízás:

utolsó váltónapok = váltás (hónap) {eset FEBRUÁR -> 28; NOVEMBER JÚNIUS, SZEPTEMBER ÁPRILIS -> 30; alapértelmezett -> 31; }

Itt használjuk a kapcsoló kifejezés egy szám generálásához, majd ezt a számot közvetlenül egy változóhoz rendeljük.

Korábban ez csak a változó definiálásával volt lehetséges napok mint nulla majd hozzárendelünk egy értéket a kapcsoló esetek. Ez azt jelentette napok nem lehet végleges, és potenciálisan nem is rendelhető, ha elmulasztanánk egy esetet.

4. Közelgő változások

Eddig ezek a változások vagy már elérhetőek, vagy a következő kiadásban lesznek. A Project Amber részeként vannak olyan javasolt változtatások, amelyek megjelenését még nem tervezték.

4.1. Nyers húros literálok

Jelenleg a Java-nak pontosan egy módja van a String literál definiálására - a tartalmat dupla idézőjelekbe foglalva. Ez könnyen használható, de bonyolultabb esetekben problémákat szenved.

Kimondottan, nehéz karaktereket tartalmazó karakterláncokat írni - beleértve, de nem kizárólag: új sorokat, dupla idézőjeleket és visszavágó karaktereket. Ez különösen problémás lehet a fájl elérési útjaiban és a reguláris kifejezéseiben, ahol ezek a karakterek gyakoribbak lehetnek, mint a tipikus.

A JEP-326 egy új karakterlánc-típust mutat be, Raw String Literals néven. Ezek kettős idézőjelek helyett backtick jelölésekbe vannak foglalva, és azokban bármilyen karaktert tartalmazhatnak.

Ez azt jelenti, hogy lehetővé válik több soron átívelő karakterláncok, valamint idézőjeleket vagy visszavonásokat tartalmazó karakterláncok írása anélkül, hogy el kellene kerülni őket. Így könnyebben olvashatók.

Például:

// Fájlrendszer elérési útja "C: \ Dev \ file.txt" "C: \ Dev \ file.txt` // Regex" \ d + \. \ d \ d "\ d + \. \ d \ d` // Többsoros "Hello \ nWorld" "Hello World"

Mindhárom esetben könnyebben belátható, hogy mi történik a verzióban a backtickekkel, amit szintén sokkal kevésbé hajlamos a gépelés.

Az új nyershúrú literálok lehetővé teszik számunkra, hogy bonyodalmak nélkül maguk is felvegyék a háttérbotokat. A karakterlánc indításához és befejezéséhez használt backtickek száma a kívánt hosszúságú lehet - nem csak egy backticknek kell lennie. A karakterlánc csak akkor ér véget, ha elérjük az azonos hosszúságú backtickeket. Tehát például:

"Ez a karaktersorozat egyetlen" "lehetőséget engedélyez, mivel két backtickbe van csomagolva."

Ezek lehetővé teszik számunkra, hogy pontosan úgy írjuk be a karakterláncokat, ahogy vannak, ahelyett, hogy bármikor szükségünk lenne speciális szekvenciákra bizonyos karakterek működéséhez.

4.2. Lambda maradványok

A JEP-302 néhány apró javítást mutat be a lambdák működésében.

A legfontosabb változások a paraméterek kezelésének módjában vannak. Először, ez a változás bevezeti azt a lehetőséget, hogy aláhúzást használjon egy fel nem használt paraméterhez, hogy ne hozzunk létre neveket, amelyekre nincs szükség. Ez korábban lehetséges volt, de csak egyetlen paraméter esetében, mivel az aláhúzás érvényes név volt.

A Java 8 bevezetett egy változást, így az aláhúzás névként történő használata figyelmeztetést jelent. A Java 9 ezt követően hibává vált, és megakadályozta, hogy egyáltalán használjuk őket. Ez a közelgő változás lehetővé teszi számukra a lambda paraméterek megadását konfliktusok nélkül. Ez lehetővé tenné például a következő kódot:

jdbcTemplate.queryForObject ("KIVÁLASZTÁS * FROM felhasználóktól WHERE user_id = 1", (rs, _) -> parseUser (rs))

Ezen fejlesztés alatt két paraméterrel definiáltuk a lambdát, de csak az első kötődik egy névhez. A második nem hozzáférhető, de ugyanúgy, azért írtuk így, mert nincs szükségünk a használatára.

A továbbfejlesztés másik nagy változása az, hogy a lambda paraméterek elárnyékolhatják a neveket az aktuális kontextustól. Ez jelenleg nem megengedett, ami miatt az ideálisnál kevesebb kódot írhatunk. Például:

Karaktersorozat = computeSomeKey (); map.computeIfAbsent (kulcs, kulcs2 -> kulcs2.hossz ());

A fordítón kívül nincs valódi szükség, miért kulcs és kulcs2 nem oszthat meg nevet. A lambdának soha nem kell hivatkoznia a változóra kulcs, és erre kényszerítve minket csúnyábbá teszi a kódot.

Ehelyett ez a továbbfejlesztés lehetővé teszi számunkra, hogy egyértelműbb és egyszerűbb módon írjuk meg:

Karaktersorozat = computeSomeKey (); map.computeIfAbsent (kulcs, kulcs -> kulcs.hossz ());

Ezenkívül van egy javasolt változás ebben a fejlesztésben, amely befolyásolhatja a túlterhelés felbontását, ha egy túlterhelt módszer lambda argumentummal rendelkezik. Jelenleg vannak olyan esetek, amikor ez kétértelműséghez vezethet a túlterhelés-feloldás működési szabályai miatt, és ez a JEP ezeket a szabályokat kissé kiigazíthatja, hogy elkerülje e kétértelműség egy részét.

Például, jelenleg a fordító a következő módszereket tartja kétértelműnek:

m (ps predikátum) {...} m (fss függvény) {...}

Mindkét módszer egy lambdát használ, amelynek egyetlen van Húr paraméter, és nincs érvénytelen visszatérési típusa. A fejlesztő számára nyilvánvaló, hogy különböznek egymástól - az egyik a-t adja vissza Húr, a másik pedig a logikai, de a fordító ezeket kétértelműként kezeli.

Ez a JEP orvosolhatja ezt a hiányosságot, és lehetővé teheti a túlterhelés kifejezett kezelését.

4.3. Mintaillesztés

A JEP-305 fejlesztéseket mutat be azzal kapcsolatban, hogy miként működhetünk együtt Például az operátor és automatikus típusú kényszer.

Jelenleg a Java-típusok összehasonlításakor a Például az operátorral, hogy lássuk, az érték a megfelelő típusú-e, majd utána a megfelelő típust kell adnunk:

if (objektum a karakterláncból) {karakterlánc s = (karakterlánc) obj; // használja s}

Ez működik és azonnal megérthető, de a szükségesnél bonyolultabb. Van néhány nagyon nyilvánvaló ismétlés a kódunkban, és ezért fennáll annak a veszélye, hogy a hibák behatolhatnak.

Ez a fejlesztés hasonló beállításokat végez a Például az operátor, amint azt korábban a erőforrásokkal próbálkozzon a Java 7-ben. Ezzel a változtatással az összehasonlítás, a leadás és a változó deklaráció egyetlen állítássá válik:

if (a String s objektum példánya) {// use s}

Ez egyetlen állítást ad nekünk, duplikáció nélkül és a hibák bekúszásának veszélye nélkül, és mégis ugyanúgy teljesít, mint a fentiek.

Ez az ágak között is megfelelően fog működni, lehetővé téve a következők működését:

if (a String s objektum példánya) {// itt használhatja az s elemet} else {// itt nem használhatja az s elemet}

A továbbfejlesztés a megfelelő hatókörhatárok között is megfelelő módon működik. A. Által deklarált változó Például az záradék a várakozásoknak megfelelően helyesen fogja árnyékolni a rajta kívül definiált változókat. Ez csak a megfelelő blokkban fog megtörténni, bár:

Karakterlánc s = "Hello"; if (obj stringof String s) {// s az obj} else-re utal {// s az if utasítás előtt definiált változóra utal

Ez ugyanazon belül is működik ha kikötés, ugyanúgy, mint amire támaszkodunk nulla ellenőrzések:

if (a String s && s.length ()> 5 objektum példánya) {// s 5 karaktert meghaladó karakterlánc}

Jelenleg ezt csak 2005 - re tervezik ha nyilatkozatok, de a jövőbeni munka valószínűleg kiterjeszti a munkára váltani a kifejezéseket is.

4.4. Tömör módszer testek

JEP tervezete 8209434 javaslat az egyszerűsített módszer-definíciók támogatására, a lambda-definíciók működéséhez hasonló módon.

Jelenleg három különböző módon definiálhatunk egy lambdát: törzzsel, egyetlen kifejezésként vagy módszer referenciaként:

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

Azonban, amikor a tényleges osztálymódszertestek megírására van szükség, ezeket jelenleg teljes egészében ki kell írnunk.

Ennek a javaslatnak az a célja, hogy ezekhez a módszerekhez is kifejezést és módszer referencia űrlapokat támogasson, azokban az esetekben, amikor alkalmazhatók. Ez segít abban, hogy bizonyos módszerek a jelenleginél sokkal egyszerűbbek legyenek.

Például egy getter módszer nem igényel teljes metódustörzset, de egyetlen kifejezéssel helyettesíthető:

String getName () -> név;

Hasonlóképpen helyettesíthetjük azokat a módszereket, amelyek egyszerűen más módszerek köré burkolva vannak, egy módszer referencia hívással, beleértve a paraméterek átadását:

int hossz (String s) = Karakterlánc :: hossz

Ezek lehetővé teszik az egyszerűbb módszereket abban az esetben, ha van értelme, ami azt jelenti, hogy kevésbé valószínű, hogy elhomályosítják a valós üzleti logikát az osztály többi részében.

Felhívjuk figyelmét, hogy ez még mindig vázlat állapotban van, és mint ilyen, a szállítás előtt jelentősen megváltozik.

5. Enhanced Enums

A JEP-301-et korábban úgy tervezték, hogy a Project Amber része legyen. Ez némi javulást hozott volna az enums-ben, kifejezetten lehetővé téve, hogy az egyes enum elemek különálló általános típusú információkkal rendelkezzenek.

Például lehetővé tenné:

enum Primitív {INT (Egész szám.osztály, 0) {int mod (int x, int y) {visszatérés x% y; } int add (int x, int y) {return x + y; }}, FLOAT (Float.class, 0f) {long add (long x, long y) {return x + y; }}, ...; final Class boxClass; végső X defaultValue; Primitív (Osztály boxClass, X defaultValue) {this.boxClass = boxClass; this.defaultValue = defaultValue; }}

Sajnálatos módon, A javítás kísérletei a Java fordító alkalmazáson belül bebizonyították, hogy kevésbé életképes, mint azt korábban gondolták. Az általános típusú információk hozzáadása az enum elemekhez lehetetlenné tette, hogy ezeket az enumokat általános típusként használhassák más osztályokban - például EnumSet. Ez drasztikusan csökkenti a kiegészítés hasznosságát.

Mint olyan, ez a fejlesztés jelenleg várakozik, amíg ezeket a részleteket ki nem lehet dolgozni.

6. Összefoglalás

Itt számos különféle funkcióval foglalkozunk. Némelyikük már elérhető, mások hamarosan elérhetők, és még többet terveznek a jövőbeni kiadásokra. Hogyan javíthatják ezek a jelenlegi és jövőbeli projektjeit?