JavaRush /Java Blog /Random-TK /ArrayList.forEach-da Lambdas we usul salgylanmalary - näh...

ArrayList.forEach-da Lambdas we usul salgylanmalary - nähili işleýär

Toparda çap edildi
Java Sintaksis Zero gözleginde lambda aňlatmalaryna giriş gaty anyk mysal bilen başlaýar:
ArrayList<string> list = new ArrayList<>();
Collections.addAll(list, "Hello", "How", "дела?");

list.forEach( (s) -> System.out.println(s) );
Leksiýanyň awtorlary “ArrayList” synpynyň standart forEach funksiýasyny ulanyp, lambdalary we usuly salgylanmalary derňeýärler. Özümde bolup geçýän zatlaryň manysyna düşünmek kyn düşdi, sebäbi bu funksiýanyň ýerine ýetirilmegi, şeýle hem interfeýs “kapotyň aşagynda” galýar. Argument (ler) nireden gelýär , println () funksiýasy nireden geçýär , özümize jogap bermeli boljak soraglar. Bagtymyza, IntelliJ IDEA bilen, “ArrayList” synpynyň iç ýüzüne aňsatlyk bilen seredip bileris we bu nahary ilkibaşdan açyp bileris. Şeýle hem, bir zada düşünmeýän bolsaňyz we düşünmek isleseňiz, bu meselede size azajyk kömek etmäge synanyşaryn. Lambda aňlatmasy we ArrayList.forEach - nähili işleýär Leksiýadan lambda aňlatmasynyň funksional interfeýsiň durmuşa geçirilýändigini eýýäm bilýäris . .Agny, bir funksiýaly interfeýsi yglan edýäris we bu funksiýanyň näme edýändigini düşündirmek üçin lambda ulanýarys. Munuň üçin size zerur: 1. Funksional interfeýs dörediň; 2. Görnüşi funksional interfeýsine laýyk gelýän üýtgeýjini dörediň; 3. Bu üýtgeýjini funksiýanyň ýerine ýetirilişini beýan edýän lambda aňlatmasyny belläň; 4. Üýtgeýjä girip, bir funksiýa jaň ediň (belki terminologiýada gödek däl, ýöne bu iň düşnükli usuldyr). Google-dan ýönekeý mysal bererin, jikme-jik düşündirişler bererin (metanit.com sahypasynyň awtorlaryna sag bolsun aýdýaryn):
interface Operationable {
    int calculate(int x, int y);
    // Единственная функция в интерфейсе — значит, это функциональный интерфейс,
    // который можно реализовать с помощью лямбды
}

public class LambdaApp {

    public static void main(String[] args) {

        // Создаём переменную operation типа Operationable (так называется наш функциональный интерфейс)
        Operationable operation;
        // Прописываем реализацию функции calculate с помощью лямбды, на вход подаём x и y, на выходе возвращаем их сумму
        operation = (x,y)->x+y;

        // Теперь мы можем обратиться к функции calculate через переменную operation
        int result = operation.calculate(10, 20);
        System.out.println(result); //30
    }
}
Indi leksiýadan mysallara gaýdyp geleliň. Sanaw ýygyndysyna String görnüşiniň birnäçe elementi goşulýar . Soňra elementler sanaw obýektinde çagyrylýan standart forEach funksiýasynyň kömegi bilen alynýar . Käbir geň parametrleri bolan lambda aňlatmasy, funksiýa argument hökmünde geçýär .
ArrayList<string> list = new ArrayList<>();
Collections.addAll(list, "Hello", "How", "дела?");

list.forEach( (s) -> System.out.println(s) );
Bu ýerde bolup geçen zatlara derrew düşünmedik bolsaňyz, ýeke dälsiňiz. Bagtymyza, IntelliJ IDEA-da ajaýyp klawiatura gysga ýoly bar: Ctrl + Çep_Mouse_Button . ForEach-a geçip, bu kombinasiýany bassak, adaty ArrayList synpynyň deslapky kody açylar, onda forEach usulynyň durmuşa geçirilişini göreris :
public void forEach(Consumer<? super E> action) {
    Objects.requireNonNull(action);
    final int expectedModCount = modCount;
    final Object[] es = elementData;
    final int size = this.size;
    for (int i = 0; modCount == expectedModCount && i < size; i++)
        action.accept(elementAt(es, i));
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}
Giriş argumentiniň Sarp edijiniň görnüşidigini görýäris . Geliň, kursory Sarp ediji sözüniň üstünden geçireliň we Ctrl + LMB jadyly kombinasiýasyny ýene bir gezek basalyň . Sarp ediji interfeýsiniň beýany açylar . Eger deslapky ýerine ýetirişi aýyrsak (bu indi biziň üçin möhüm däl), aşakdaky kody göreris:
public interface Consumer<t> {
   void accept(T t);
}
Şeýlelik bilen Islendik görnüşdäki bir argumenti kabul edýän ýekeje kabul ediş funksiýasy bolan Sarp ediji interfeýsi bar . Diňe bir funksiýa bolansoň, interfeýs işleýär we ony ýerine ýetirmek lambda aňlatmasy arkaly ýazylyp bilner. ArrayList-iň “ForEach” funksiýasynyň bardygyny, eýýäm sarp ediji interfeýsini hereket argumenti hökmünde ýerine ýetirýändigini gördük . Mundan başga-da, forEach funksiýasynda aşakdaky kody tapýarys:
for (int i = 0; modCount == expectedModCount && i < size; i++)
    action.accept(elementAt(es, i));
“Loop”, “ArrayList” -iň ähli elementleri arkaly gaýtalanýar. Aýlawyň içinde hereket obýektiniň kabul ediş funksiýasyna jaň görýäris - operasiýa.calculate diýip nädip atlandyranymyzy ýadyňyzdamy? Theygyndynyň häzirki elementi kabul etmek funksiýasyna geçirilýär . Indi ahyrsoňy lambda asyl aňlatmasyna gaýdyp gelip, onuň näme edýändigine düşünip bileris. Thehli kodlary bir üýşmeleňde ýygnalyň:
public interface Consumer<t> {
   void accept(T t); // Функция, которую мы реализуем лямбда-выражением
}

public void forEach(Consumer<? super E> action) // В action хранится an object Consumer, в котором функция accept реализована нашей лямбдой {
    Objects.requireNonNull(action);
    final int expectedModCount = modCount;
    final Object[] es = elementData;
    final int size = this.size;
    for (int i = 0; modCount == expectedModCount && i < size; i++)
        action.accept(elementAt(es, i)); // Вызываем нашу реализацию функции accept интерфейса Consumer для каждого element коллекции
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

//...

list.forEach( (s) -> System.out.println(s) );
Lambda aňlatmamyz, Sarp ediji interfeýsinde beýan edilen kabul ediş funksiýasynyň ýerine ýetirilmegidir . Lambda ulanyp, kabul etmek funksiýasynyň argument s alýandygyny we ony ekranda görkezýändigini kesgitledik . Lambda aňlatmasy, sarp ediji interfeýsiniň ýerine ýetirilişini saklaýan hereket argumenti hökmünde forEach funksiýasyna geçirildi . Indi forEach funksiýasy, sarp ediji interfeýsiniň ýerine ýetirilişini şuňa meňzeş setir bilen çagyryp biler:
action.accept(elementAt(es, i));
Şeýlelik bilen, lambda aňlatmasyndaky giriş argumenti, Sarp ediji interfeýsini durmuşa geçirmegimize geçen ArrayList ýygyndysynyň başga bir elementidir . Bularyň hemmesi: ArrayList.forEach-da lambda aňlatmasynyň logikasyny analiz etdik. ArrayList.forEach-da bir usula salgylanma - bu nähili işleýär? Leksiýanyň indiki ädimi usuly salgylanmalara seretmekdir. Dogry, muňa gaty geň düşünýärler - leksiýany okanymdan soň, bu koduň näme edýändigine düşünip bilmedim:
list.forEach( System.out::println );
Ilki bilen ýene azajyk teoriýa. Usul salgylanmasy , takmynan aýdylanda, başga bir funksiýa bilen beýan edilen funksional interfeýsiň durmuşa geçirilmegi . Againene-de ýönekeý bir mysal bilen başlaryn:
public interface Operationable {
    int calculate(int x, int y);
    // Единственная функция в интерфейсе — значит, это функциональный интерфейс
}

public static class Calculator {
    // Создадим статический класс Calculator и пропишем в нём метод methodReference.
    // Именно он будет реализовывать функцию calculate из интерфейса Operationable.
    public static int methodReference(int x, int y) {
        return x+y;
    }
}

public static void main(String[] args) {
    // Создаём переменную operation типа Operationable (так называется наш функциональный интерфейс)
    Operationable operation;
    // Теперь реализацией интерфейса будет не лямбда-выражение, а метод methodReference из нашего класса Calculator
    operation = Calculator::methodReference;

    // Теперь мы можем обратиться к функции интерфейса через переменную operation
    int result = operation.calculate(10, 20);
    System.out.println(result); //30
}
Leksiýadan mysallara gaýdyp geleliň:
list.forEach( System.out::println );
“System.out” -yň “println” funksiýasyna eýe bolan “PrintStream” görnüşli obýektdigini ýatladýaryn . Geliň, println-iň üstünden geçeliň we Ctrl + LMB basyň :
public void println(String x) {
    if (getClass() == PrintStream.class) {
        writeln(String.valueOf(x));
    } else {
        synchronized (this) {
            print(x);
            newLine();
        }
    }
}
Iki esasy aýratynlyga üns bereliň: 1. “println” funksiýasy hiç zady yzyna gaýtarmaýar (boş). 2. “println” funksiýasy giriş hökmünde bir argument alýar. Size hiç zady ýatlatmaýarmy?
public interface Consumer<t> {
   void accept(T t);
}
Dogry - kabul etmek funksiýasynyň goly, println usuly golunyň has umumy ýagdaýy ! Bu, ikinjisine bir usula salgylanma hökmünde üstünlikli ulanylyp bilinjekdigini aňladýar, ýagny println kabul ediş funksiýasynyň belli bir ýerine ýetirilişine öwrülýär :
list.forEach( System.out::println );
ForEach funksiýasyna argument hökmünde System.out obýektiniň println funksiýasyny geçdik . Principleörelge lambda bilen deňdir: indi forEach ýygnamak elementini action.accept (elementAt (es, i)) jaňy arkaly println funksiýasyna geçirip biler . Aslynda muny indi System.out.println (elementAt (es, i)) görnüşinde okap bolýar .
public void forEach(Consumer<? super E> action) // В action хранится an object Consumer, в котором функция accept реализована методом println {
        Objects.requireNonNull(action);
        final int expectedModCount = modCount;
        final Object[] es = elementData;
        final int size = this.size;
        for (int i = 0; modCount == expectedModCount && i < size; i++)
            action.accept(elementAt(es, i)); // Функция accept теперь реализована методом System.out.println!
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
Lambdalara we usuly salgylanmalara täze gelenler üçin ýagdaýy iň bolmanda azajyk düşündirdim diýip umyt edýärin. Sözümiň ahyrynda, Robert Şildtiň meşhur "Java: Bir başlangyç gollanma" kitabyny maslahat berýärin - meniň pikirimçe, lambdalar we funksiýa salgylanmalary bu ýerde gaty manyly beýan edilýär.
Teswirler
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION