Programavimas

Pradėkite nuo „lambda“ išraiškų „Java“

Prieš „Java SE 8“ anoniminės klasės paprastai buvo naudojamos funkcijoms perduoti. Ši praktika sugadino šaltinio kodą, todėl jį buvo sunkiau suprasti. „Java 8“ pašalino šią problemą įdiegdama „lambdas“. Šioje pamokoje pirmiausia pristatoma lambda kalbos funkcija, tada pateikiama išsamesnė funkcinio programavimo su lambda išraiškomis ir tikslinių tipų įžanga. Jūs taip pat sužinosite, kaip lambdas sąveikauja su taikymo sritimis, vietiniais kintamaisiais, tai ir super raktinius žodžius ir „Java“ išimtis.

Atminkite, kad šios mokymo programos kodų pavyzdžiai yra suderinami su JDK 12.

Atrasti sau tipus

Šioje pamokoje nepateiksiu jokių ne lambda kalbos ypatybių, apie kurias jūs dar nežinojote, bet aš pademonstruosiu lambdas per tipus, kurių anksčiau nesu aptaręs šioje serijoje. Vienas iš pavyzdžių yra java.lang.Math klasė. Šiuos tipus pristatysiu būsimose „Java 101“ pamokose. Kol kas siūlau perskaityti JDK 12 API dokumentus, kad sužinotumėte daugiau apie juos.

atsisiųsti Gauti kodą Atsisiųskite šaltinį, pvz., programas, šioje pamokoje. Sukūrė Jeffas Friesenas, skirtas „JavaWorld“.

Lambdas: Gruntas

A lambda išraiška (lambda) apibūdina kodo bloką (anoniminę funkciją), kurį galima perduoti konstruktoriams ar metodams, kad jie būtų vykdomi vėliau. Konstruktorius ar metodas gauna lambdą kaip argumentą. Apsvarstykite šį pavyzdį:

() -> System.out.println („Labas“)

Šis pavyzdys identifikuoja „lambda“, skirtą pranešimui išleisti į standartinį išvesties srautą. Iš kairės į dešinę, () identifikuoja oficialų „lambda“ parametrų sąrašą (pavyzdyje nėra parametrų), -> rodo, kad išraiška yra lambda, ir System.out.println („Labas“) yra vykdytinas kodas.

Lambdas supaprastina funkcinės sąsajos, kurios yra anotuotos sąsajos, kuriose kiekvienas deklaruoja tiksliai vieną abstraktų metodą (nors jie taip pat gali deklaruoti bet kokį numatytųjų, statinių ir privačių metodų derinį). Pavyzdžiui, standartinėje klasės bibliotekoje yra a java.lang. Bėgama sąsaja su viena santrauka niekinis bėgimas () metodas. Šios funkcinės sąsajos deklaracija pateikiama žemiau:

@FunctionalInterface viešoji sąsaja Paleidžiama {public abstract void run (); }

Klasės biblioteka anotuoja Bėgama su @ Funkcinė sąsaja, kuris yra java.lang.FunctionalInterface anotacijos tipas. Funkcinė sąsaja yra naudojamas anotuoti tas sąsajas, kurios bus naudojamos lambda kontekstuose.

„Lambda“ neturi aiškaus sąsajos tipo. Vietoj to, kompiliatorius, naudodamas aplinkinį kontekstą, daro išvadą, kurią funkcinę sąsają akimirksniu nustatyti, kai nurodoma lambda - lambda yra surišti prie tos sąsajos. Pavyzdžiui, tarkime, kad nurodžiau šį kodo fragmentą, kuris perduoda ankstesnę „lambda“ kaip argumentą „ java.lang.Twread klasės Siūlas (paleidžiamas taikinys) konstruktorius:

nauja gija (() -> System.out.println („Labas“));

Kompiliatorius nustato, kad lambda yra perduodama Siūlas (Runnable r) nes tai vienintelis konstruktorius, kuris tenkina lambda: Bėgama yra funkcinė sąsaja, „lambda“ tuščias oficialus parametrų sąrašas () degtukai paleisti ()tuščias parametrų sąrašas ir grąžinimo tipai (tuštuma) taip pat sutinku. Lambda yra susieta Bėgama.

1 sąraše pateikiamas mažos programos šaltinio kodas, leidžiantis žaisti su šiuo pavyzdžiu.

Sąrašas 1. „LambdaDemo.java“ (1 versija)

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

Sudaryti 1 sąrašą (javac LambdaDemo.java) ir paleiskite programą (java LambdaDemo). Turėtumėte stebėti šį rezultatą:

Sveiki

„Lambdas“ gali labai supaprastinti šaltinio kodo kiekį, kurį turite parašyti, ir taip pat daug lengviau suprasti šaltinio kodą. Pvz., Be „lambdas“, tikriausiai nurodytumėte daugiau 2 sąrašo kodą, pagrįstą anoniminės klasės egzemplioriumi, kuris įgyvendina Bėgama.

2 sąrašas. LambdaDemo.java (2 versija)

public class LambdaDemo {public static void main (String [] args) {Runnable r = new Runnable () {@Paisyti public void run () {System.out.println ("Sveiki"); }}; nauja gija (r) .start (); }}

Sudarę šį šaltinio kodą, paleiskite programą. Jūs atrasite tą patį išvestį, kuris buvo parodytas anksčiau.

„Lambdas“ ir srautų API

Taip pat supaprastindami šaltinio kodą, „lambdas“ vaidina svarbų vaidmenį „Java“ funkciškai orientuotame srautų API. Jie apibūdina funkcionalumo vienetus, kurie perduodami įvairiems API metodams.

Java lambdas gilumoje

Norėdami efektyviai naudoti lambdas, turite suprasti lambda išraiškų sintaksę ir tikslinio tipo sąvoką. Jūs taip pat turite suprasti, kaip lambdas sąveikauja su taikymo sritimis, vietiniais kintamaisiais, tai ir super raktinius žodžius ir išimtis. Aš apžvelgsiu visas šias temas tolesniuose skyriuose.

Kaip įgyvendinamos lambdos

„Lambdas“ yra įdiegtos „Java“ virtualiosios mašinos požiūriu iškviestas dinamiškas instrukcija ir java.lang.invoke API. Norėdami sužinoti apie lambda architektūrą, žiūrėkite vaizdo įrašą „Lambda: žvilgsnis po gaubtu“.

Lambda sintaksė

Kiekviena lambda atitinka šią sintaksę:

( formalusis parametrų sąrašas ) -> { išraiška-ar-teiginiai }

formalusis parametrų sąrašas yra kableliais atskirtas oficialių parametrų sąrašas, kuris turi atitikti funkcinės sąsajos vieno abstraktaus metodo parametrus vykdymo metu. Jei praleisite jų tipus, kompiliatorius daro išvadą, kad šie tipai yra iš konteksto, kuriame naudojama lambda. Apsvarstykite šiuos pavyzdžius:

(dvigubas a, dvigubas b) // aiškiai nurodyti tipai (a, b) // kompiliatoriaus numanomi tipai

Lambdas ir var

Pradėdami nuo „Java SE 11“, tipo pavadinimą galite pakeisti var. Pavyzdžiui, galite nurodyti (var a, var b).

Privalote nurodyti skliaustus, jei nenorite kelių oficialių parametrų arba jų nėra. Tačiau nurodydami vieną oficialų parametrą, skliaustelių galite praleisti (nors to ir neturite). (Tai taikoma tik parametro pavadinimui - skliausteliai būtini, kai taip pat nurodomas tipas.) Apsvarstykite šiuos papildomus pavyzdžius:

x // skliaustai praleisti dėl vieno formalaus parametro (dvigubi x) // reikalingi skliaustai, nes yra ir tipas () // skliaustai reikalingi, kai nėra formalių parametrų (x, y)

formalusis parametrų sąrašas seka a -> žetonas, po kurio seka išraiška-ar-teiginiai- išraiškos ar sakinių blokas (arba žinomas kaip lambda kūnas). Skirtingai nuo išraiškos kūnų, teiginiais pagrįsti kūnai turi būti dedami tarp atvirų ({) ir uždarykite (}) petnešos simboliai:

(dvigubas spindulys) -> Math.PI * spindulys * spindulio spindulys -> {grąžina Math.PI * spindulį * spindulį; } spindulys -> {System.out.println (spindulys); grąžinti Math.PI * spindulį * spindulį; }

Pirmojo pavyzdžio išraiška pagrįstas lambda korpusas neturi būti dedamas tarp petnešų. Antrasis pavyzdys išraiška pagrįstą kūną paverčia teiginiu paremtu kūnu, kuriame grįžti turi būti nurodyta, kad būtų grąžinta išraiškos vertė. Paskutinis pavyzdys rodo kelis teiginius ir negali būti išreikštas be petnešų.

Lambda kūnai ir kabliataškiai

Atkreipkite dėmesį į tai, ar nėra kabliataškių (;) ankstesniuose pavyzdžiuose. Kiekvienu atveju lambda kūnas nenutraukiamas kabliataškiu, nes lambda nėra teiginys. Tačiau teiginyje pagrįstame lambda kūne kiekvienas sakinys turi būti baigtas kabliataškiu.

3 sąraše pateikiama paprasta programa, parodanti lambda sintaksę; atkreipkite dėmesį, kad šis sąrašas remiasi dviem ankstesniais kodų pavyzdžiais.

3 sąrašas. „LambdaDemo.java“ (3 versija)

@FunctionalInterface sąsaja BinaryCalculator {dvigubai apskaičiuoti (dviguba reikšmė1, dviguba reikšmė2); } @ Funkcinė sąsaja „UnaryCalculator“ {dvigubai apskaičiuoti (dviguba reikšmė); } public class LambdaDemo {public static void main (String [] args) {System.out.printf ("18 + 36,5 =% f% n", apskaičiuokite ((double v1, double v2) -> v1 + v2, 18, 36.5)); System.out.printf ("89 / 2,9 =% f% n", apskaičiuokite ((v1, v2) -> v1 / v2, 89, 2.9)); System.out.printf ("- 89 =% f% n", apskaičiuokite (v -> -v, 89)); System.out.printf ("18 * 18 =% f% n", apskaičiuokite ((dvigubas v) -> v * v, 18)); } statinis dvigubas skaičiavimas (BinaryCalculator calc, double v1, double v2) {return calc.calculate (v1, v2); } statinis dvigubas skaičiavimas („UnaryCalculator calc“, dvigubas v) {return calc.calculate (v); }}

3 sąraše pirmiausia pristatoma „BinaryCalculator“ ir „UnaryCalculator“ funkcinės sąsajos, kurių apskaičiuoti() metodai atlieka skaičiavimus atitinkamai dviem įvesties argumentais arba vienu įvesties argumentu. Šiame sąraše taip pat pristatomas a „LambdaDemo“ klasė, kurios pagrindinis () metodas demonstruoja šias funkcines sąsajas.

Funkcinės sąsajos parodytos statinis dvigubas skaičiavimas („BinaryCalculator calc“, dvigubas v1, dvigubas v2) ir statinis dvigubas skaičiavimas („UnaryCalculator calc“, dvigubas v) metodai. Lambdas perduoda kodą kaip duomenis šiems metodams, kurie gaunami kaip „BinaryCalculator“ arba „UnaryCalculator“ atvejų.

Sudarykite 3 sąrašą ir paleiskite programą. Turėtumėte stebėti šį rezultatą:

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

Tiksliniai tipai

Lambda siejama su implicitine taikinio tipas, kuris identifikuoja objekto, su kuriuo yra susieta lambda, tipą. Taikinio tipas turi būti funkcinė sąsaja, iš kurios daroma išvada iš konteksto, todėl lambdas negali būti rodomas tokiuose kontekstuose:

  • Kintama deklaracija
  • Užduotis
  • Grąžinimo pareiškimas
  • Masyvo inicializatorius
  • Metodo ar konstruktoriaus argumentai
  • Lambda korpusas
  • Trikampė sąlyginė išraiška
  • Aktorių išraiška

4 sąraše pateikiama programa, kuri parodo šiuos tikslinio tipo kontekstus.

4 sąrašas. „LambdaDemo.java“ (4 versija)

importuoti java.io.File; importuoti java.io.FileFilter; importuoti java.nio.file.Files; importuoti java.nio.file.FileSystem; importuoti java.nio.file.FileSystems; importuoti java.nio.file.FileVisitor; importuoti java.nio.file.FileVisitResult; importuoti java.nio.file.Path; importuoti java.nio.file.PathMatcher; importuoti java.nio.file.Paths; importuoti java.nio.file.SimpleFileVisitor; importuoti java.nio.file.attribute.BasicFileAttributes; importuoti java.security.AccessController; importuoti java.security.PrivilegedAction; importuoti java.util.Arrays; importuoti java.util.Collections; importuoti java.util.Comparator; importuoti java.util.List; importuoti java.util.concurrent.Callable; public class LambdaDemo {public static void main (String [] args) throws Exception {// Tikslinis tipas # 1: kintamojo deklaracija Runnable r = () -> {System.out.println ("veikia"); }; r.run (); // Tikslinis tipas Nr. 2: priskyrimas r = () -> System.out.println ("veikia"); r.run (); // Tikslinis tipas Nr. 3: grąžinimo sakinys (faile getFilter ()) Failas [] failai = naujas failas ("."). ListFiles (getFilter ("txt")); for (int i = 0; i path.toString (). endWith ("txt"), (path) -> path.toString (). endWith ("java")}; FileVisitor visitor; visitor = new SimpleFileVisitor () { @Paisyti viešąjį „FileVisitResult“ visitFile (kelio failas, „BasicFileAttributes“ atributai) {kelio pavadinimas = file.getFileName (); for (int i = 0; i System.out.println ("veikia")). Start (); // Tikslinio tipo # 6: „lambda“ kūnas (įdėta lambda) Skambintinas iškviečiamas = () -> () -> System.out.println ("vadinamas"); callable.call (). Run (); // Tikslinis tipas # 7: trijų sąlyginė išraiška boolean ascendingSort = false; Palyginamasis cmp; cmp = (ascendingSort)? (s1, s2) -> s1.compareTo (s2): (s1, s2) -> s2.compareTo (s1); Išvardykite miestus = Arrays.asList ("Vašingtonas", "Londonas", "Roma", "Berlynas", "Jeruzalė", "Otava", "Sidnėjus", "Maskva"); Rinkiniai. Rūšiuoti (miestai, cmp); už (int i = 0; i <cities.size (); i ++) System.out.println (cities.get (i)); // Tikslinis tipas Nr. 8: išraiškos eilutė vartotojas = AccessController.doPrivileged ((PrivilegedAction) () -> System.getProperty ("Vartotojo vardas ")); System.out.println (vartotojas); } static FileFilter getFilter (String ext) {return (pathname) -> pathname.toString (). endWith (ext); }}