JavaRush /Блоги Java /Random-TG /Ламбдаҳо ва истинодҳои методӣ дар ArrayList.forEach - он ...

Ламбдаҳо ва истинодҳои методӣ дар ArrayList.forEach - он чӣ гуна кор мекунад

Дар гурӯҳ нашр шудааст
Муқаддима ба ифодаҳои ламбда дар ҷустуҷӯи Java Syntax Zero бо як мисоли мушаххас оғоз мешавад:
ArrayList<string> list = new ArrayList<>();
Collections.addAll(list, "Hello", "How", "дела?");

list.forEach( (s) -> System.out.println(s) );
Муаллифони лексия ламбдаҳо ва истинодҳои методиро бо истифода аз функсияи стандартии forEach синфи ArrayList таҳлил мекунанд. Шахсан ман фаҳмидам, ки маънои он чӣ рӯй дода истодааст, душвор буд, зеро татбиқи ин функсия ва инчунин интерфейси бо он алоқаманд "дар зери сарпӯш" боқӣ мемонад. Дар куҷо аргумент (ҳо) аз , ки функсияи println() интиқол дода мешавад , саволҳое ҳастанд, ки мо бояд худамон ҷавоб диҳем. Хушбахтона, бо IntelliJ IDEA, мо метавонем ба осонӣ ба дохor синфи ArrayList назар кунем ва ин угро аз аввал кушоем. Агар шумо низ чизе нафаҳмед ва хоҳед, ки онро фаҳмед, ман кӯшиш мекунам, ки ба шумо дар ин ҳадди аққал каме кӯмак расонам. Ифодаи Lambda ва ArrayList.forEach - чӣ тавр кор мекунад Аз лексия мо аллакай медонем, ки ифодаи ламбда амалисозии интерфейси функсионалӣ мебошад . Яъне, мо интерфейсро бо як функсия эълон мекунем ва барои тавсифи кори ин функсия ламбдаро истифода мебарем. Барои ин ба шумо лозим аст: 1. Интерфейси функсионалӣ эҷод кунед; 2. Таѓйирёбандаеро созед, ки навъи он ба интерфейси функсионалї мувофиќ бошад; 3. Ба ин тағирёбанда ифодаи ламбда таъин кунед, ки иҷрои функсияро тавсиф мекунад; 4. Функсияро тавассути дастрасӣ ба тағирёбанда даъват кунед (шояд ман дар истилоҳот дағалона рафтор мекунам, аммо ин роҳи равшантарин аст). Ман як мисоли оддиеро аз Google меорам, ки онро бо шарҳҳои муфассал пешниҳод мекунам (ташаккур ба муаллифони сайти metanit.com):
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
    }
}
Акнун ба мисоли лекция бармегардем. Якчанд унсурҳои навъи String ба коллексияи рӯйхат илова карда мешаванд . Сипас унсурҳо бо истифода аз функсияи стандартии forEach , ки дар an objectи рӯйхат даъват карда мешавад, гирифта мешаванд . Ифодаи ламбда бо баъзе параметрҳои аҷиби s ҳамчун аргумент ба функсия интиқол дода мешавад .
ArrayList<string> list = new ArrayList<>();
Collections.addAll(list, "Hello", "How", "дела?");

list.forEach( (s) -> System.out.println(s) );
Агар шумо дарҳол нафаҳмида бошед, ки дар ин ҷо чӣ рӯй дод, пас шумо танҳо нестед. Хушбахтона, IntelliJ IDEA як миёнабури аълои клавиатура дорад: Ctrl+Left_Mouse_Button . Агар мо курсорро болои forEach гузорем ва ин комбинатсияро пахш кунем, рамзи сарчашмаи синфи стандартии ArrayList кушода мешавад, ки дар он мо татбиқи усули forEach -ро мебинем :
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();
}
Мо мебинем, ки аргументи вуруд амали навъи Consumer аст . Биёед курсорро ба болои калимаи Consumer гузаронем ва комбинатсияи ҷодугарӣ Ctrl+LMB -ро бори дигар пахш кунем . Тавсифи интерфейси истеъмолкунанда кушода мешавад . Агар мо татбиқи пешфарзро аз он хориҷ кунем (ин барои мо ҳоло муҳим нест), мо рамзи зеринро мебинем:
public interface Consumer<t> {
   void accept(T t);
}
Пас. Мо интерфейси истеъмолкунанда дорем , ки дорои як функсияи қабул , ки як аргументи ҳама гуна намудҳоро қабул мекунад. Азбаски танҳо як функсия вуҷуд дорад, пас интерфейс функсионалӣ аст ва татбиқи онро тавассути ифодаи ламбда навиштан мумкин аст. Мо аллакай дидем, ки ArrayList дорои функсияи forEach мебошад , ки татбиқи интерфейси истеъмолиро ҳамчун далели амал қабул мекунад . Илова бар ин, дар функсияи forEach мо рамзи зеринро пайдо мекунем:
for (int i = 0; modCount == expectedModCount && i < size; i++)
    action.accept(elementAt(es, i));
Давраи for аслан тавассути тамоми унсурҳои ArrayList такрор мешавад. Дар дохor давра мо зангро ба функсияи қабули an objectи амал мебинем - дар хотир доред, ки чӣ тавр мо Operation.calculate номида будем? Унсури ҷории коллексия ба функсияи қабул интиқол дода мешавад . Акнун мо метавонем дар ниҳоят ба ифодаи аслии лямбда баргардем ва фаҳмем, ки он чӣ кор мекунад. Биёед ҳамаи рамзҳоро дар як чӯб ҷамъ кунем:
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) );
Ифодаи лямбда мо амалисозии функсияи қабул аст , ки дар интерфейси истеъмолӣ тавсиф шудааст . Бо истифода аз ламбда, мо муайян кардем, ки функсияи қабул аргументи s-ро мегирад ва онро дар экран нишон медиҳад. Ифодаи лямбда ба функсияи forEach ҳамчун далели амали он интиқол дода шуд , ки татбиқи интерфейси истеъмолиро нигоҳ медорад . Акнун функсияи forEach метавонад татбиқи интерфейси истеъмолии моро бо чунин сатр даъват кунад:
action.accept(elementAt(es, i));
Ҳамин тариқ, аргументи вуруди s дар ифодаи лямбда унсури дигари коллексияи ArrayList мебошад, ки ба татбиқи интерфейси истеъмолии мо интиқол дода мешавад . Ин ҳама: мо мантиқи ифодаи лямбдаро дар ArrayList.forEach таҳлил кардем. Истинод ба усул дар ArrayList.forEach - он чӣ гуна кор мекунад? Қадами навбатӣ дар лексия аз назар гузаронии истинодҳои методӣ мебошад. Дуруст аст, ки онҳо инро ба таври хеле аҷиб мефаҳманд - пас аз хондани лексия ман имкони фаҳмидани он набуд, ки ин code чӣ кор мекунад:
list.forEach( System.out::println );
Аввалан, боз як назарияи каме. Истиноди метод , ба таври дағалона гӯем, татбиқи интерфейси функсионалӣ мебошад, ки онро функсияи дигар тавсиф мекунад . Боз, ман бо як мисоли оддӣ оғоз мекунам:
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
}
Биёед ба мисоли лексия бармегардем:
list.forEach( System.out::println );
Ёдовар мешавам, ки System.out an objectи навъи PrintStream аст, ки функсияи println дорад . Биёед курсорро болои println гузаронем ва Ctrl+LMB -ро пахш кунем :
public void println(String x) {
    if (getClass() == PrintStream.class) {
        writeln(String.valueOf(x));
    } else {
        synchronized (this) {
            print(x);
            newLine();
        }
    }
}
Биёед ду хусусияти асосиро қайд кунем: 1. Функсияи println чизе барнамегардонад (беэътибор). 2. Функсияи println як аргументро ҳамчун вуруд қабул мекунад. Оё ба шумо чизеро хотиррасон намекунад?
public interface Consumer<t> {
   void accept(T t);
}
Ин дуруст аст - имзои функсияи қабул як ҳолати умумии имзои усули println аст ! Ин маънои онро дорад, ки охирин метавонад бомуваффақият ҳамчун истинод ба усул истифода шавад - яъне println татбиқи мушаххаси функсияи қабул мегардад :
list.forEach( System.out::println );
Мо вазифаи println-и an objectи System.out -ро ҳамчун далел ба функсияи forEach гузаштем . Принсипи лямбда якхела аст: ҳоло forEach метавонад тавассути занги action.accept(elementAt(es, i)) унсури коллексияро ба функсияи println гузаронад . Дар асл, ин ҳоло метавонад ҳамчун System.out.println(elementAt(es, i)) хонда шавад .
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();
    }
Умедворам, ки ман вазъиятро ҳадди аққал барои онҳое, ки ба ламбдаҳо ва истинодҳои методӣ нав ҳастанд, равшан кардаам. Хулоса, ман китоби машҳури "Java: Дастури шурӯъкунандагон" -и Роберт Шилдтро тавсия медиҳам - ба андешаи ман, дар он ламбдаҳо ва истинодҳои функсияҳо хеле оқилона тасвир шудаанд.
Шарҳҳо
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION