Ang Java sa simula ay isang ganap na object-oriented na wika. Maliban sa mga primitive na uri, ang lahat sa Java ay isang bagay. Kahit na ang mga array ay mga bagay. Ang mga pagkakataon ng bawat klase ay mga bagay. Walang isang solong posibilidad na tukuyin ang isang function nang hiwalay (sa labas ng isang klase - tinatayang transl. ). At walang paraan upang maipasa ang isang pamamaraan bilang argumento o ibalik ang katawan ng pamamaraan bilang resulta ng isa pang pamamaraan. Parang ganun. Ngunit ito ay bago ang Java 8. Dahil ang mga araw ng magandang lumang Swing, ito ay kinakailangan upang magsulat ng mga hindi kilalang klase kapag ito ay kinakailangan upang ipasa ang ilang functionality sa ilang paraan. Halimbawa, ganito ang hitsura ng pagdaragdag ng isang event handler:
someObject.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
//Event listener implementation goes here...
}
});
Dito gusto naming magdagdag ng ilang code sa tagapakinig ng kaganapan ng mouse. Tinukoy namin ang isang hindi kilalang klase MouseAdapter
at agad na lumikha ng isang bagay mula dito. Sa ganitong paraan, naipasa namin ang karagdagang paggana sa addMouseListener
. Sa madaling salita, hindi madaling ipasa ang isang simpleng pamamaraan (functionality) sa Java sa pamamagitan ng mga argumento. Pinilit ng limitasyong ito ang mga developer ng Java 8 na magdagdag ng tampok tulad ng mga expression ng Lambda sa detalye ng wika.
Bakit kailangan ng Java ang mga expression ng Lambda?
Sa simula pa lang, ang wikang Java ay hindi gaanong nagbago, maliban sa mga bagay tulad ng Anotasyon, Generics, atbp. Una sa lahat, ang Java ay palaging nananatiling object-oriented. Pagkatapos magtrabaho sa mga functional na wika tulad ng JavaScript, mauunawaan ng isa kung paano mahigpit na object-oriented ang Java at malakas ang pag-type. Hindi kailangan ang mga function sa Java. Sa kanilang sarili, hindi sila matatagpuan sa mundo ng Java. Sa functional programming language, nauuna ang mga function. Sila ay umiiral sa kanilang sarili. Maaari mong italaga ang mga ito sa mga variable at ipasa ang mga ito sa pamamagitan ng mga argumento sa iba pang mga function. Ang JavaScript ay isa sa mga pinakamahusay na halimbawa ng functional programming language. Makakahanap ka ng magagandang artikulo sa Internet na nagdedetalye ng mga benepisyo ng JavaScript bilang isang functional na wika. Ang mga functional na wika ay may makapangyarihang mga tool tulad ng Closure, na nagbibigay ng ilang mga pakinabang sa mga tradisyonal na paraan ng pagsulat ng mga application. Ang pagsasara ay isang function na may environment na naka-attach dito - isang table na nag-iimbak ng mga reference sa lahat ng hindi lokal na variable ng function. Sa Java, ang mga pagsasara ay maaaring gayahin sa pamamagitan ng mga expression ng Lambda. Siyempre, may mga pagkakaiba sa pagitan ng mga pagsasara at mga expression ng Lambda, at hindi mga maliliit, ngunit ang mga expression ng lambda ay isang magandang alternatibo sa mga pagsasara. Sa kanyang mapanukso at nakakatawang blog, inilalarawan ni Steve Yegge kung paano mahigpit na nakatali ang mundo ng Java sa mga pangngalan (mga entidad, bagay - tinatayang transl. ). Kung hindi mo pa nababasa ang kanyang blog, inirerekumenda ko ito. Inilalarawan niya sa nakakatawa at kawili-wiling paraan ang eksaktong dahilan kung bakit idinagdag ang mga expression ng Lambda sa Java. Ang mga expression ng Lambda ay nagdadala ng functionality sa Java na matagal nang nawawala. Ang mga expression ng Lambda ay nagdadala ng functionality sa wika tulad ng mga bagay. Bagama't hindi ito 100% totoo, makikita mo na ang mga expression ng Lambda, habang hindi mga pagsasara, ay nagbibigay ng mga katulad na kakayahan. Sa isang functional na wika, ang mga expression ng lambda ay mga function; ngunit sa Java, ang mga expression ng lambda ay kinakatawan ng mga bagay, at dapat na nauugnay sa isang partikular na uri ng bagay na tinatawag na isang functional na interface. Susunod ay titingnan natin kung ano ito. Ang artikulo ni Mario Fusco na "Bakit kailangan natin ng Lambda Expression sa Java" ay naglalarawan nang detalyado kung bakit ang lahat ng modernong wika ay nangangailangan ng mga kakayahan sa pagsasara.Panimula sa Lambda Expressions
Ang mga expression ng Lambda ay mga hindi kilalang function (maaaring hindi isang 100% tamang kahulugan para sa Java, ngunit nagdudulot ito ng kaunting kalinawan). Sa madaling salita, ito ay isang paraan na walang deklarasyon, i.e. walang mga modifier ng access, nagbabalik na halaga at pangalan. Sa madaling salita, pinapayagan ka nilang magsulat ng isang paraan at gamitin ito kaagad. Ito ay lalong kapaki-pakinabang sa kaso ng isang beses na paraan ng tawag, dahil binabawasan ang oras na kinakailangan upang magdeklara at magsulat ng isang pamamaraan nang hindi kinakailangang lumikha ng isang klase. Ang mga expression ng Lambda sa Java ay karaniwang may sumusunod na syntax(аргументы) -> (тело)
. Halimbawa:
(арг1, арг2...) -> { тело }
(тип1 арг1, тип2 арг2...) -> { тело }
Nasa ibaba ang ilang halimbawa ng totoong Lambda expression:
(int a, int b) -> { return a + b; }
() -> System.out.println("Hello World");
(String s) -> { System.out.println(s); }
() -> 42
() -> { return 3.1415 };
Istraktura ng Lambda Expressions
Pag-aralan natin ang istruktura ng mga expression ng lambda:- Ang mga expression ng Lambda ay maaaring magkaroon ng 0 o higit pang mga parameter ng pag-input.
- Ang uri ng mga parameter ay maaaring tahasang tukuyin o maaaring makuha mula sa konteksto. Halimbawa (
int a
) ay maaaring isulat ng ganito (a
) - Ang mga parameter ay nakapaloob sa panaklong at pinaghihiwalay ng mga kuwit. Halimbawa (
a, b
) o (int a, int b
) o (String a
,int b
,float c
) - Kung walang mga parameter, kailangan mong gumamit ng mga walang laman na panaklong. Halimbawa
() -> 42
- Kapag mayroon lamang isang parameter, kung ang uri ay hindi tahasang tinukoy, ang mga panaklong ay maaaring tanggalin. Halimbawa:
a -> return a*a
- Ang katawan ng isang Lambda expression ay maaaring maglaman ng 0 o higit pang mga expression.
- Kung ang katawan ay binubuo ng isang pahayag, maaaring hindi ito nakapaloob sa mga kulot na brace, at maaaring tukuyin ang return value nang wala ang keyword
return
. - Kung hindi, ang mga kulot na brace ay kinakailangan (block ng code) at ang return value ay dapat na tukuyin sa dulo gamit ang isang keyword
return
(kung hindi, ang return type ayvoid
).
Ano ang isang functional na interface
Sa Java, ang mga interface ng Marker ay mga interface nang hindi nagdedeklara ng mga pamamaraan o field. Sa madaling salita, ang mga interface ng token ay mga walang laman na interface. Katulad nito, ang Mga Functional Interface ay mga interface na may isang abstract na paraan lamang na ipinahayag dito.java.lang.Runnable
ay isang halimbawa ng isang functional na interface. Nagpahayag lamang ito ng isang paraan void run()
. Mayroon ding isang interface ActionListener
- gumagana din. Noong nakaraan, kailangan naming gumamit ng mga hindi kilalang klase upang lumikha ng mga bagay na nagpapatupad ng isang functional na interface. Sa mga expression ng Lambda, naging mas simple ang lahat. Ang bawat lambda expression ay maaaring implicitly na nakatali sa ilang functional na interface. Halimbawa, maaari kang lumikha ng isang sanggunian sa Runnable
isang interface, tulad ng ipinapakita sa sumusunod na halimbawa:
Runnable r = () -> System.out.println("hello world");
Ang ganitong uri ng conversion ay palaging ginagawa nang tahasan kapag hindi kami tumukoy ng functional na interface:
new Thread(
() -> System.out.println("hello world")
).start();
Sa halimbawa sa itaas, awtomatikong gumagawa ang compiler ng lambda expression bilang pagpapatupad Runnable
ng interface mula sa class constructor Thread
: public Thread(Runnable r) { }
. Narito ang ilang halimbawa ng mga expression ng lambda at kaukulang functional interface:
Consumer<Integer> c = (int x) -> { System.out.println(x) };
BiConsumer<Integer, String> b = (Integer x, String y) -> System.out.println(x + " : " + y);
Predicate<String> p = (String s) -> { s == null };
Ang annotation @FunctionalInterface
na idinagdag sa Java 8 ayon sa Java Language Specification ay nagsusuri kung gumagana ang ipinahayag na interface. Bilang karagdagan, ang Java 8 ay may kasamang bilang ng mga nakahanda nang functional na interface para magamit sa mga expression ng Lambda. @FunctionalInterface
ay magtapon ng error sa compilation kung hindi gumagana ang ipinahayag na interface. Ang sumusunod ay isang halimbawa ng pagtukoy ng isang functional na interface:
@FunctionalInterface
public interface WorkerInterface {
public void doSomeWork();
}
Tulad ng iminumungkahi ng kahulugan, ang isang functional na interface ay maaaring magkaroon lamang ng isang abstract na pamamaraan. Kung susubukan mong magdagdag ng isa pang abstract na paraan, makakakuha ka ng error sa compilation. Halimbawa:
@FunctionalInterface
public interface WorkerInterface {
public void doSomeWork();
public void doSomeMoreWork();
}
Error
Unexpected @FunctionalInterface annotation @FunctionalInterface ^ WorkerInterface is not a functional interface multiple non-overriding abstract methods found in interface WorkerInterface 1 error После определения функционального интерфейса, мы можем его использовать и получать все преимущества Lambda-выражений. Пример:
// defining a functional interface @FunctionalInterface public interface WorkerInterface { public void doSomeWork(); }
public class WorkerInterfaceTest {
public static void execute(WorkerInterface worker) {
worker.doSomeWork();
}
public static void main(String [] args) {
// calling the doSomeWork method via an anonymous class
// (classic)
execute(new WorkerInterface() {
@Override
public void doSomeWork() {
System.out.println("Worker called via an anonymous class");
}
});
// calling the doSomeWork method via Lambda expressions
// (Java 8 new)
execute( () -> System.out.println("Worker called via Lambda") );
}
}
Konklusyon:
Worker вызван через анонимный класс
Worker вызван через Lambda
Dito namin tinukoy ang aming sariling functional interface at gumamit ng lambda expression. Ang pamamaraan execute()
ay may kakayahang tanggapin ang mga expression ng lambda bilang isang argumento.
Mga halimbawa ng Lambda expression
Ang pinakamahusay na paraan upang maunawaan ang mga expression ng Lambda ay ang tumingin sa ilang mga halimbawa:Thread
Maaaring masimulan ang isang stream sa dalawang paraan:
// Old way:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello from thread");
}
}).start();
// New way:
new Thread(
() -> System.out.println("Hello from thread")
).start();
Ang pamamahala ng kaganapan sa Java 8 ay maaari ding gawin sa pamamagitan ng mga expression ng Lambda. Ang sumusunod ay dalawang paraan upang magdagdag ng tagapangasiwa ng kaganapan ActionListener
sa isang bahagi ng UI:
// Old way:
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button pressed. Old way!");
}
});
// New way:
button.addActionListener( (e) -> {
System.out.println("Button pressed. Lambda!");
});
Isang simpleng halimbawa ng pagpapakita ng lahat ng elemento ng isang naibigay na array. Tandaan na mayroong higit sa isang paraan upang gumamit ng lambda expression. Sa ibaba ay gumagawa kami ng lambda expression sa karaniwang paraan gamit ang arrow syntax, at ginagamit din namin ang double colon operator (::)
, na sa Java 8 ay nagko-convert ng isang regular na paraan sa isang lambda expression:
// Old way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for(Integer n: list) {
System.out.println(n);
}
// New way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.forEach(n -> System.out.println(n));
// New way using double colon operator ::
list.forEach(System.out::println);
Sa sumusunod na halimbawa, gumagamit kami ng functional na interface Predicate
para gumawa ng pagsubok at mag-print ng mga item na pumasa sa pagsubok na iyon. Sa ganitong paraan maaari kang maglagay ng lohika sa mga expression ng lambda at gumawa ng mga bagay batay dito.
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class Main {
public static void main(String [] a) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
System.out.print("Outputs all numbers: ");
evaluate(list, (n)->true);
System.out.print("Does not output any number: ");
evaluate(list, (n)->false);
System.out.print("Output even numbers: ");
evaluate(list, (n)-> n%2 == 0 );
System.out.print("Output odd numbers: ");
evaluate(list, (n)-> n%2 == 1 );
System.out.print("Output numbers greater than 5: ");
evaluate(list, (n)-> n > 5 );
}
public static void evaluate(List<Integer> list, Predicate<Integer> predicate) {
for(Integer n: list) {
if(predicate.test(n)) {
System.out.print(n + " ");
}
}
System.out.println();
}
}
Konklusyon:
Выводит все числа: 1 2 3 4 5 6 7
Не выводит ни одного числа:
Вывод четных чисел: 2 4 6
Вывод нечетных чисел: 1 3 5 7
Вывод чисел больше 5: 6 7
Sa pamamagitan ng pag-iisip sa mga expression ng Lambda, maaari mong ipakita ang parisukat ng bawat elemento ng listahan. Pansinin na ginagamit namin ang paraan stream()
upang i-convert ang isang regular na listahan sa isang stream. Ang Java 8 ay nagbibigay ng isang kahanga-hangang klase Stream
( java.util.stream.Stream
). Naglalaman ito ng napakaraming kapaki-pakinabang na pamamaraan na magagamit mo sa mga expression ng lambda. Nagpapasa kami ng lambda expression x -> x*x
sa method map()
, na nalalapat ito sa lahat ng elemento sa stream. Pagkatapos nito ay ginagamit namin forEach
upang i-print ang lahat ng mga elemento ng listahan.
// Old way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
for(Integer n : list) {
int x = n * n;
System.out.println(x);
}
// New way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
list.stream().map((x) -> x*x).forEach(System.out::println);
Dahil sa isang listahan, kailangan mong i-print ang kabuuan ng mga parisukat ng lahat ng mga elemento ng listahan. Nagbibigay-daan sa iyo ang mga expression ng Lambda na makamit ito sa pamamagitan ng pagsusulat lamang ng isang linya ng code. Ang halimbawang ito ay gumagamit ng convolution (reduction) method reduce()
. Gumagamit kami ng isang paraan map()
upang i-square ang bawat elemento, at pagkatapos ay gumamit ng isang paraan reduce()
upang i-collapse ang lahat ng mga elemento sa isang solong numero.
// Old way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = 0;
for(Integer n : list) {
int x = n * n;
sum = sum + x;
}
System.out.println(sum);
// New way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = list.stream().map(x -> x*x).reduce((x,y) -> x + y).get();
System.out.println(sum);
Ang pagkakaiba sa pagitan ng mga expression ng Lambda at mga hindi kilalang klase
Ang pangunahing pagkakaiba ay ang paggamit ng keywordthis
. Para sa mga anonymous na klase, ang keyword na ' ' this
ay tumutukoy sa isang object ng anonymous na klase, habang sa isang lambda expression, ' this
' ay tumutukoy sa isang object ng klase kung saan ginagamit ang lambda expression. Ang isa pang pagkakaiba ay ang paraan ng pagsasama-sama ng mga ito. Kino-compile ng Java ang mga expression ng lambda at kino-convert ang mga ito sa private
mga pamamaraan ng klase. Ito ay gumagamit ng invokedynamic na pagtuturo , na ipinakilala sa Java 7 para sa dynamic na paraan na nagbubuklod. Inilarawan ni Tal Weiss sa kanyang blog kung paano kino-compile ng Java ang mga expression ng lambda sa bytecode
GO TO FULL VERSION