Sobes.tech
Middle+

Ламбда билдирүүлөрү кандай иштейт жана 'капоттун астында' эмне болот?

sobes.tech AI

AIден жооп

Javaда лямбда-экспрессиялар төмөнкү механизмдер аркылуу иштейт:

  1. Функционалдык интерфейстер: Лямбда-экспрессия өзгөрмөгө берилиши, методтон кайтарылышы же методго өткөрүлүшү керек, анын тиби функционалдык интерфейс болушу керек. Функционалдык интерфейс — бул бир гана абстракттуу методду (SAM - Single Abstract Method) камтыган интерфейс. @FunctionalInterface аннотациясы милдеттүү эмес, бирок компилятор тарабынан текшерүү үчүн сунушталат.

  2. Инвокация динамикасы (invokeDynamic): Байткоддо аноним ички класс түзүүнүн ордуна, Java invokeDynamic командасын колдонуп жатат. Бул команда Java 7де JVMдеги динамикалык типтелген тилдерди колдоо үчүн кошулду жана Java 8де лямбда-экспрессиялар жана метод шилтемелерин ишке ашыруу үчүн колдонулат. invokeDynamic чакыруу убактысында (runtime) чечилет.

Негизги иштөө процесси:

Компиляция учурунда:

  • Компилятор лямбда-экспрессияны анализдейт.
  • Лямбдага ылайык келген функционалдык интерфейс тиби аныкталат.
  • Лямбда логикасы камтылган атайын синтетикалык метод (көбүнчө lambda$ префикси менен) ошол класста түзүлөт. Бул метод абстракттуу метод менен бирдей сигнатурага ээ.
  • Лямбда колдонулган жеринде компилятор invokeDynamic командасын генерейт. Бул команда bootstrap method (LambdaMetafactory.metafactory) шилтемесин камтыйт.

Ишке ашыруу учурунда:

  • JVM invokeDynamic командасын көргөндө, ал биринчи жолу bootstrap method-ти чакырат.
  • bootstrap method (LambdaMetafactory.metafactory) динамикалык түрдө жадда call site түзөт.
  • Чакыруучу жүктөгүч (call site) функционалдык интерфейсин ишке ашыратган классинин экземплярин түзүп, кайтарат. Ал экземпляр:
    • түзүлгөн (мисалы, Constant CallSite аркылуу),
    • кэштелген (стейтлесс лямбда үчүн),
    • же мурдагы экземплярга шилтеме катары көрсөтүлүшү мүмкүн.
  • Бул түзүлгөн класс абстракттуу методду генерирленген синтетикалык методго (lambda$) делегаттайт.
  • Биринчи bootstrap чакыргандан кийин, call site кайсы объект керек экенин эстеп калат жана кийинки invokeDynamic чакыруулары тезирек ошол объектти алууга же түздөн-түз чакырууга мүмкүндүк берет.

Ошентип, лямбда-экспрессиялар ар бир инстанция үчүн жаңы класс түзбөйт. Стейтлесс лямбда-экспрессиялар үчүн, функционалдык интерфейсдин реализациясы singleton же кэштелген инстанс болушу мүмкүн. Стейттүү лямбда-экспрессиялар жаңы экземпляр түзүүнү талап кылат, ал захваченные өзгөрмөлөрдү сактайт.

invokeDynamic колдонуу JIT-кампиляторго функциялык интерфейс методдорунун чакырууларын натыйжалуу оптимизациялоого мүмкүндүк берет, анткени JIT ишке ашыруу учурунда көбүрөөк маалыматка ээ болот.

// Функционалдык интерфейс мисалы
@FunctionalInterface
interface MyFunction {
    String apply(String input);
}

public class LambdaExample {
    public static void main(String[] args) {
        // Лямбда-экспрессияны колдонуу
        MyFunction func = s -> "Processed: " + s;

        String result = func.apply("Test");
        System.out.println(result);
    }
}