- Para sa mga nagbabasa ng unang bahagi ng artikulong ito;
- Para sa mga nag-iisip na alam na nila ang Java Core, ngunit walang ideya tungkol sa mga expression ng lambda sa Java. O, marahil, may narinig ka na tungkol sa mga lambda, ngunit walang mga detalye.
- para sa mga may kaunting pag-unawa sa mga expression ng lambda, ngunit natatakot at hindi pangkaraniwang gamitin ang mga ito.
Pag-access sa mga panlabas na variable
Mag-compile ba ang code na ito sa isang hindi kilalang klase?int counter = 0;
Runnable r = new Runnable() {
@Override
public void run() {
counter++;
}
};
Hindi. Ang variable counter
ay dapat na final
. O hindi naman final
, ngunit sa anumang kaso hindi nito mababago ang halaga nito. Ang parehong prinsipyo ay ginagamit sa mga expression ng lambda. Mayroon silang access sa lahat ng mga variable na "nakikita" sa kanila mula sa lugar kung saan sila idineklara. Ngunit hindi dapat baguhin ng lambda ang mga ito (magtalaga ng bagong halaga). Totoo, may opsyon na laktawan ang limitasyong ito sa mga hindi kilalang klase. Ito ay sapat lamang upang lumikha ng isang variable ng isang uri ng sanggunian at baguhin ang panloob na estado ng bagay. Sa kasong ito, ang variable mismo ay ituturo sa parehong bagay, at sa kasong ito maaari mong ligtas na ipahiwatig ito bilang final
.
final AtomicInteger counter = new AtomicInteger(0);
Runnable r = new Runnable() {
@Override
public void run() {
counter.incrementAndGet();
}
};
Narito ang aming variable counter
ay isang reference sa isang object ng uri AtomicInteger
. At upang baguhin ang estado ng bagay na ito, ginagamit ang pamamaraan incrementAndGet()
. Ang halaga ng variable mismo ay hindi nagbabago habang tumatakbo ang program at palaging tumuturo sa parehong bagay, na nagbibigay-daan sa amin na magdeklara kaagad ng variable gamit ang keyword final
. Ang parehong mga halimbawa, ngunit may mga expression ng lambda:
int counter = 0;
Runnable r = () -> counter++;
Hindi ito nag-compile para sa parehong dahilan tulad ng opsyon na may hindi kilalang klase: counter
hindi ito dapat magbago habang tumatakbo ang programa. Ngunit tulad nito - maayos ang lahat:
final AtomicInteger counter = new AtomicInteger(0);
Runnable r = () -> counter.incrementAndGet();
Nalalapat din ito sa mga paraan ng pagtawag. Mula sa loob ng isang lambda expression, hindi mo lamang maa-access ang lahat ng "nakikita" na mga variable, ngunit matatawag din ang mga pamamaraan na mayroon kang access.
public class Main {
public static void main(String[] args) {
Runnable runnable = () -> staticMethod();
new Thread(runnable).start();
}
private static void staticMethod() {
System.out.println("Я - метод staticMethod(), и меня только-что кто-то вызвал!");
}
}
Bagama't pribado ang pamamaraan staticMethod()
, ito ay "naa-access" na tawagan sa loob ng pamamaraan main()
, kaya naa-access din itong tumawag mula sa loob ng lambda na nilikha sa pamamaraan main
.
Ang sandali ng pagpapatupad ng lambda expression code
Ang tanong na ito ay maaaring mukhang masyadong simple sa iyo, ngunit ito ay nagkakahalaga ng pagtatanong: kailan ang code sa loob ng lambda expression ay isasagawa? Sa sandali ng paglikha? O sa sandaling (hindi pa rin alam kung saan) tatawagin? Ito ay medyo madali upang suriin.System.out.println("Запуск программы");
// много всякого разного codeа
// ...
System.out.println("Перед объявлением лямбды");
Runnable runnable = () -> System.out.println("Я - лямбда!");
System.out.println("После объявления лямбды");
// много всякого другого codeа
// ...
System.out.println("Перед передачей лямбды в тред");
new Thread(runnable).start();
Output na ipinapakita:
Запуск программы
Перед объявлением лямбды
После объявления лямбды
Перед передачей лямбды в тред
Я - лямбда!
Makikita na ang lambda expression code ay naisakatuparan sa pinakadulo, pagkatapos malikha ang thread at kapag ang proseso ng pagpapatupad ng programa ay umabot sa aktwal na pagpapatupad ng pamamaraan run()
. At hindi sa oras ng anunsyo nito. Sa pamamagitan ng pagdedeklara ng lambda expression, gumawa lang kami ng object ng uri Runnable
at inilarawan ang gawi ng pamamaraan nito run()
. Ang pamamaraan mismo ay inilunsad sa ibang pagkakataon.
Mga Sanggunian sa Pamamaraan?
Hindi direktang nauugnay sa mga lambdas mismo, ngunit sa palagay ko ay lohikal na magsabi ng ilang salita tungkol dito sa artikulong ito. Sabihin nating mayroon tayong lambda expression na walang ginagawang espesyal, ngunit tumatawag lang ng ilang paraan.x -> System.out.println(x)
May inabot sila sa kanya х
at pasimpleng tinawag siya nito System.out.println()
at nilagpasan siya doon х
. Sa kasong ito, maaari naming palitan ito ng isang link sa paraang kailangan namin. Ganito:
System.out::println
Oo, nang walang panaklong sa dulo! Mas kumpletong halimbawa:
List<String> strings = new LinkedList<>();
strings.add("Mother");
strings.add("soap");
strings.add("frame");
strings.forEach(x -> System.out.println(x));
Sa huling linya gumagamit kami ng isang paraan forEach()
na tumatanggap ng isang interface object Consumer
. Ito ay muli ng isang functional na interface na may isang paraan lamang void accept(T t)
. Alinsunod dito, nagsusulat kami ng lambda expression na tumatagal ng isang parameter (dahil ito ay nai-type sa interface mismo, hindi namin ipinapahiwatig ang uri ng parameter, ngunit ipinapahiwatig na ito ay tatawagin х)
. Sa katawan ng lambda expression isinusulat namin ang code iyon ay isasagawa kapag ang pamamaraan ay tinawag na accept()
. Dito ay ipinapakita lang namin sa screen kung ano ang nasa variable х
. Ang pamamaraan mismo forEach()
ay dumaan sa lahat ng mga elemento ng koleksyon, tinatawag Consumer
ang paraan ng interface na bagay na ipinasa dito (aming lambda) accept()
, kung saan ipinapasa nito ang bawat elemento mula sa koleksyon. Gaya ng nasabi ko na, ito ay isang lambda -expression (pagtawag lamang ng ibang pamamaraan) maaari nating palitan ng isang reference sa pamamaraang kailangan natin. Pagkatapos ang ating code ay magiging ganito:
List<String> strings = new LinkedList<>();
strings.add("Mother");
strings.add("soap");
strings.add("frame");
strings.forEach(System.out::println);
Ang pangunahing bagay ay ang tinatanggap na mga parameter ng mga pamamaraan (println()
at accept())
. Dahil ang pamamaraan println()
ay maaaring tumanggap ng anumang bagay (ito ay na-overload para sa lahat ng mga primitive at para sa anumang mga bagay), sa halip na isang lambda expression, maaari naming ipasa sa isang forEach()
sanggunian lamang sa pamamaraan println()
. Pagkatapos forEach()
ay kukunin ang bawat elemento ng koleksyon at ipapasa ito nang direkta sa println()
Para sa mga nakatagpo nito sa unang pagkakataon, pakitandaan Mangyaring tandaan na hindi namin tinatawag ang pamamaraan (na may mga tuldok sa pagitan System.out.println()
ng mga salita at may mga bracket sa dulo), ngunit sa halip ay ipinapasa namin ang reference sa paraang ito mismo.
strings.forEach(System.out.println());
magkakaroon tayo ng compilation error. Dahil bago ang tawag forEach()
, makikita ng Java na ito ay tinatawag System.out.println()
, mauunawaan nito na ito ay ibinabalik void
at susubukan void
na ipasa ito sa forEach()
object ng uri na naghihintay doon Consumer
.
Syntax para sa paggamit ng Method References
Ito ay medyo simple:-
Pagpasa ng reference sa isang static na pamamaraan
NameКласса:: NameСтатическогоМетода?
public class Main { public static void main(String[] args) { List<String> strings = new LinkedList<>(); strings.add("Mother"); strings.add("soap"); strings.add("frame"); strings.forEach(Main::staticMethod); } private static void staticMethod(String s) { // do something } }
-
Pagpasa ng reference sa isang non-static na pamamaraan gamit ang isang umiiral na bagay
NameПеременнойСОбъектом:: method name
public class Main { public static void main(String[] args) { List<String> strings = new LinkedList<>(); strings.add("Mother"); strings.add("soap"); strings.add("frame"); Main instance = new Main(); strings.forEach(instance::nonStaticMethod); } private void nonStaticMethod(String s) { // do something } }
-
Nagpapasa kami ng reference sa isang non-static na pamamaraan gamit ang klase kung saan ipinatupad ang naturang pamamaraan
NameКласса:: method name
public class Main { public static void main(String[] args) { List<User> users = new LinkedList<>(); users.add(new User("Vasya")); users.add(new User("Коля")); users.add(new User("Петя")); users.forEach(User::print); } private static class User { private String name; private User(String name) { this.name = name; } private void print() { System.out.println(name); } } }
-
Ang pagpasa ng isang link sa constructor
NameКласса::new
Ang paggamit ng mga link ng pamamaraan ay napaka-maginhawa kapag mayroong isang handa na paraan na ganap kang nasisiyahan, at nais mong gamitin ito bilang isang callback. Sa kasong ito, sa halip na magsulat ng lambda expression na may code ng paraang iyon, o isang lambda expression kung saan tinatawag lang natin ang paraang ito, nagpapasa lang tayo ng reference dito. Iyon lang.
Kawili-wiling pagkakaiba sa pagitan ng anonymous na klase at lambda expression
Sa isang hindi kilalang klase, ang keywordthis
ay tumuturo sa isang bagay ng hindi kilalang klaseng iyon. At kung gagamitin natin ito this
sa loob ng isang lambda, makakakuha tayo ng access sa object ng framing class. Kung saan talaga namin isinulat ang expression na ito. Nangyayari ito dahil ang mga expression ng lambda, kapag pinagsama-sama, ay nagiging isang pribadong paraan ng klase kung saan nakasulat ang mga ito. Hindi ko inirerekumenda ang paggamit ng "tampok" na ito, dahil mayroon itong side effect, na sumasalungat sa mga prinsipyo ng functional programming. Ngunit ang diskarte na ito ay medyo pare-pareho sa OOP. ;)
Saan ko nakuha ang impormasyon o kung ano pa ang babasahin
- Tutorial sa opisyal na website ng Oracle . Marami, sa detalye, na may mga halimbawa, ngunit sa Ingles.
- Ang parehong "Oracle" na tutorial , ngunit ang kabanata ay tungkol sa Mga Sanggunian ng Paraan.
- Isang artikulo sa Habré tungkol sa functional programming (pagsasalin ng isa pang artikulo). Napakakaunting mga multi-libro tungkol sa mga lambdas, dahil ang mga ito ay tungkol sa functional programming sa pangkalahatan.
- Para sa mga gustong makaalis sa Wikipedia .
- At, siyempre, nakakita ako ng maraming bagay sa Google :)
GO TO FULL VERSION