Programiranje

Začnite z lambda izrazi v Javi

Pred Java SE 8 so se anonimni razredi običajno uporabljali za posredovanje funkcionalnosti metodi. Ta praksa je zakrila izvorno kodo, zaradi česar je težje razumeti. Java 8 je to težavo odpravila z uvedbo lambdas. Ta vadnica najprej uvede funkcijo lambda jezika, nato pa podrobneje predstavi funkcionalno programiranje z lambda izrazi skupaj s ciljnimi vrstami. Naučili se boste tudi, kako lambde delujejo z obsegi, lokalnimi spremenljivkami to in super ključne besede in izjeme Java.

Upoštevajte, da so primeri kode v tej vadnici združljivi z JDK 12.

Odkrivanje vrst zase

V tej vadnici ne bom predstavil nobenih ne-lambda jezikovnih značilnosti, za katere še niste izvedeli, bom pa lambde predstavil s tipi, o katerih prej nisem razpravljal v tej seriji. En primer je java.lang.Math razred. Te vrste bom predstavil v prihodnjih vajah o Javi 101. Za zdaj predlagam, da preberete dokumentacijo API-ja JDK 12, če želite izvedeti več o njih.

prenos Prenesite kodo Prenesite izvorno kodo, na primer programe v tej vadnici. Ustvaril Jeff Friesen za JavaWorld.

Lambde: Primer

A lambda izraz (lambda) opisuje blok kode (anonimna funkcija), ki se lahko posreduje konstruktorjem ali metodam za nadaljnje izvajanje. Konstruktor ali metoda prejme lambdo kot argument. Upoštevajte naslednji primer:

() -> System.out.println ("Pozdravljeni")

Ta primer identificira lambda za izpis sporočila v standardni izhodni tok. Od leve proti desni, () identificira lambda formalni seznam parametrov (v primeru ni parametrov), -> označuje, da je izraz lambda, in System.out.println ("Pozdravljeni") je koda, ki jo je treba izvršiti.

Lambda poenostavlja uporabo funkcionalni vmesniki, ki so pripomnjeni vmesniki, ki vsaka razglasi natančno eno abstraktno metodo (čeprav lahko prijavijo tudi katero koli kombinacijo privzetih, statičnih in zasebnih metod). Na primer, standardna knjižnica razredov ponuja java.lang.Teče vmesnik z enim samim povzetkom void run () metoda. Izjava tega funkcionalnega vmesnika je prikazana spodaj:

@FunctionalInterface javni vmesnik Runnable {javni povzetek void run (); }

Knjižnica predavanj označuje Teče s @FunctionalInterface, ki je primerek java.lang.FunctionalInterface vrsta pripisa. Funkcionalni vmesnik se uporablja za označevanje tistih vmesnikov, ki bodo uporabljeni v lambda kontekstih.

Lambda nima eksplicitne vrste vmesnika. Namesto tega prevajalnik uporablja okoliški kontekst, da ugotovi, kateri funkcionalni vmesnik naj ustvari, ko je podana lambda - lambda je vezan na ta vmesnik. Denimo, da sem na primer navedel naslednji fragment kode, ki prejšnjo lambdo posreduje kot argument datoteki java.lang.Nit razredov Navoj (cilj, ki ga je mogoče izvesti) konstruktor:

nova nit (() -> System.out.println ("Pozdravljeni"));

Prevajalnik določi, da se prenaša lambda Navoj (izvedljivo r) ker je to edini konstruktor, ki ustreza lambda: Teče je funkcionalen vmesnik, lambda prazen seznam formalnih parametrov () tekme teči ()prazen seznam parametrov in vrste vrnitve (praznino) se tudi strinjajo. Lambda je zavezana Teče.

Seznam 1 predstavlja izvorno kodo majhni aplikaciji, ki vam omogoča igranje s tem primerom.

Seznam 1. LambdaDemo.java (različica 1)

javni razred LambdaDemo {public static void main (String [] args) {new Thread (() -> System.out.println ("Hello")). start (); }}

Sestavi seznam 1 (javac LambdaDemo.java) in zaženite aplikacijo (java LambdaDemo). Upoštevati morate naslednje rezultate:

zdravo

Lambda lahko močno poenostavi količino izvorne kode, ki jo morate napisati, in lahko tudi veliko lažje razume izvorno kodo. Na primer, brez lambdas bi verjetno navedli bolj podrobno kodo na seznamu 2, ki temelji na primerku anonimnega razreda, ki izvaja Teče.

Seznam 2. LambdaDemo.java (različica 2)

javni razred LambdaDemo {public static void main (String [] args) {Runnable r = new Runnable () {@Override public void run () {System.out.println ("Hello"); }}; nova nit (r) .start (); }}

Po prevajanju te izvorne kode zaženite aplikacijo. Odkrili boste enak izhod kot prej prikazan.

Lambda in API Streams

Poleg poenostavitve izvorne kode imajo lambde pomembno vlogo v Java-jevem funkcionalno usmerjenem API-ju Streams. Opisujejo enote funkcionalnosti, ki se posredujejo različnim metodam API.

Java lambdas v globino

Za učinkovito uporabo lambdas morate razumeti sintakso lambda izrazov skupaj s pojmom ciljne vrste. Razumeti morate tudi, kako lambde vplivajo na obseg, lokalne spremenljivke, to in super ključne besede in izjeme. Vse te teme bom obravnaval v naslednjih poglavjih.

Kako se izvajajo lambde

Lambda se izvaja v smislu Java navideznih strojev invokedynamic navodila in java.lang.invoke API. Oglejte si video Lambda: Peek Under the Hood, če želite izvedeti več o lambda arhitekturi.

Lambda sintaksa

Vsaka lambda je v skladu z naslednjo skladnjo:

( seznam formalnih parametrov ) -> { izraz ali izjave }

The seznam formalnih parametrov je seznam formalnih parametrov, ločenih z vejicami, ki se morajo med izvajanjem ujemati s parametri posamezne abstraktne metode funkcionalnega vmesnika. Če izpustite njihove tipe, jih prevajalnik sklepa iz konteksta, v katerem se uporablja lambda. Upoštevajte naslednje primere:

(double a, double b) // tipi, ki so izrecno določeni (a, b) // vrste, ki jih je določil prevajalnik

Lambda in var

Začenši z Javo SE 11, lahko ime tipa nadomestite z var. Lahko na primer določite (var a, var b).

Navesti morate oklepaje za več ali nobenih formalnih parametrov. Vendar lahko oklepaje izpustite (čeprav vam ni treba) pri podajanju enega samega formalnega parametra. (To velja samo za ime parametra - oklepaji so obvezni, če je naveden tudi tip.) Upoštevajte naslednje dodatne primere:

x // oklepaji izpuščeni zaradi enojnega formalnega parametra (dvojni x) // zahtevajo se oklepaji, ker je prisoten tudi tip () // oklepaji so potrebni, kadar zaradi več formalnih parametrov niso potrebni formalni parametri (x, y)

The seznam formalnih parametrov sledi a -> žeton, ki mu sledi izraz ali izjave- izraz ali blok stavkov (bodisi je znano kot telo lambde). Za razliko od teles, ki temeljijo na izrazu, morajo biti telesa, ki temeljijo na izjavah, postavljena med odprta ({) in zaprite (}) oklepaji:

(dvojni polmer) -> Math.PI * polmer * radij polmera -> {vrni Math.PI * polmer * polmer; } polmer -> {System.out.println (radij); vrnitev Math.PI * polmer * polmer; }

Telesa lambda, ki temelji na izrazu prvega primera, ni treba postaviti med oklepaje. Drugi primer pretvori telo, ki temelji na izrazu, v telo, ki temelji na stavku, v katerem vrnitev mora biti podan, da vrne vrednost izraza. Končni primer prikazuje več izjav in jih ni mogoče izraziti brez oklepajev.

Lambda telesa in podpičja

Upoštevajte odsotnost ali prisotnost podpičja (;) v prejšnjih primerih. V obeh primerih se telo lambde ne zaključi s podpičjem, ker lambda ni stavek. V lambda telesu, ki temelji na stavku, pa je treba vsak stavek zaključiti s podpičjem.

Seznam 3 predstavlja preprosto aplikacijo, ki prikazuje lambda sintakso; upoštevajte, da ta seznam temelji na prejšnjih dveh primerih kode.

Seznam 3. LambdaDemo.java (različica 3)

@FunctionalInterface vmesnik BinaryCalculator {dvojni izračun (dvojna vrednost1, dvojna vrednost2); } @FunctionalInterface vmesnik UnaryCalculator {dvojni izračun (dvojna vrednost); } javni razred LambdaDemo {public static void main (String [] args) {System.out.printf ("18 + 36,5 =% f% n", izračunaj ((dvojni v1, dvojni v2) -> v1 + v2, 18, 36,5)); System.out.printf ("89 / 2,9 =% f% n", izračunaj ((v1, v2) -> v1 / v2, 89, 2,9)); System.out.printf ("- 89 =% f% n", izračunaj (v -> -v, 89)); System.out.printf ("18 * 18 =% f% n", izračunaj ((dvojno v) -> v * v, 18)); } statični dvojni izračun (BinaryCalculator calc, double v1, double v2) {return calc.calculate (v1, v2); } statični dvojni izračun (UnaryCalculator calc, double v) {return calc.calculate (v); }}

Seznam 3 najprej predstavlja BinaryCalculator in UnaryCalculator funkcionalni vmesniki, katerih izračunaj () metode izvajajo izračune na dveh vhodnih argumentih oziroma na enem vhodnem argumentu. Ta seznam predstavlja tudi a LambdaDemo razred, katerega glavni () metoda prikazuje te funkcionalne vmesnike.

Funkcionalni vmesniki so predstavljeni v statični dvojni izračun (izračun BinaryCalculator, dvojni v1, dvojni v2) in statični dvojni izračun (izračun UnaryCalculator, dvojni v) metode. Lambde posredujejo kodo kot podatke tem metodam, ki jih prejmejo kot BinaryCalculator ali UnaryCalculator primerov.

Sestavite seznam 3 in zaženite aplikacijo. Upoštevati morate naslednje rezultate:

18 + 36.5 = 54.500000 89 / 2.9 = 30.689655 -89 = -89.000000 18 * 18 = 324.000000

Vrste ciljev

Lambda je povezana z implicitnim ciljni tip, ki identificira vrsto predmeta, na katerega je vezana lambda. Ciljni tip mora biti funkcionalni vmesnik, ki izhaja iz konteksta, ki omejuje lambde na pojav v naslednjih kontekstih:

  • Deklaracija spremenljivke
  • Dodelitev
  • Izjava o vrnitvi
  • Pobudnik matrike
  • Argumenti metode ali konstruktorja
  • Lambda telo
  • Ternarni pogojni izraz
  • Igrani izraz

V seznamu 4 je predstavljena aplikacija, ki prikazuje te kontekste ciljne vrste.

Seznam 4. LambdaDemo.java (različica 4)

import java.io.File; uvoz java.io.FileFilter; uvoz java.nio.file.Files; uvoz java.nio.file.FileSystem; uvoz java.nio.file.FileSystems; uvoz java.nio.file.FileVisitor; uvoz java.nio.file.FileVisitResult; uvoz java.nio.file.Path; uvoz java.nio.file.PathMatcher; uvoz java.nio.file.Paths; uvoz java.nio.file.SimpleFileVisitor; uvoz java.nio.file.attribute.BasicFileAttributes; uvoz java.security.AccessController; uvoz java.security.PrivilegedAction; uvoz java.util.Arrays; uvoz java.util.Collections; uvoz java.util.Comparator; uvoz java.util.List; uvoz java.util.concurrent.Callable; javni razred LambdaDemo {public static void main (String [] args) vrže izjemo {// Tip cilja # 1: deklaracija spremenljivke Runnable r = () -> {System.out.println ("teče"); }; r.run (); // Tip cilja # 2: dodelitev r = () -> System.out.println ("teče"); r.run (); // Tip cilja # 3: vrnitev stavka (v getFilter ()) File [] files = nova datoteka ("."). ListFiles (getFilter ("txt")); for (int i = 0; i path.toString (). ENDWith ("txt"), (path) -> path.toString (). EndWith ("java")}; Obiskovalec FileVisitor; Obiskovalec = nov SimpleFileVisitor () { @Override public FileVisitResult visitFile (datoteka poti, atributi BasicFileAttributes) {Ime poti = file.getFileName (); for (int i = 0; i System.out.println ("running")). Start (); // Vrsta cilja # 6: lambda telo (ugnezdena lambda) Callable callable = () -> () -> System.out.println ("pozvan"); callable.call (). Run (); // Tip cilja # 7: ternarni pogojni izraz boolean ascendingSort = false; primerjalnik cmp; cmp = (ascendingSort)? (s1, s2) -> s1.compareTo (s2): (s1, s2) -> s2.compareTo (s1); Seznam mest = Arrays.asList ("Washington", "London", "Rim", "Berlin", "Jeruzalem", "Ottawa", "Sydney", "Moskva"); Collections.sort (mest, cmp); za (int i = 0; i <cities.size (); i ++) System.out.println (city.get (i)); // Tip cilja # 8: izraz izraza String user = AccessController.doPrivileged ((PrivilegedAction) () -> System.getProperty ("uporabniško.ime ")); System.out.println (uporabnik); } static FileFilter getFilter (String ext) {return (pathname) -> pathname.toString (). EndWith (ext); }}
$config[zx-auto] not found$config[zx-overlay] not found