Hogyan működnek a lambda kifejezések és mi történik 'a motorháztető alatt'?
sobes.tech MI
Válasz az MI-től
A Java-ban a lambda kifejezések a következő mechanizmusokon keresztül működnek:
-
Funkcionális interfészek: A lambda kifejezés egy változóhoz rendelendő, egy metódusból visszatérő vagy metódusnak átadott, amelynek típusa funkcionális interfész. A funkcionális interfész egy olyan interfész, amely pontosan egy absztrakt metódust tartalmaz (SAM - Single Abstract Method). Az
@FunctionalInterfaceannotáció opcionális, de ajánlott a fordító általi ellenőrzéshez. -
Dinamikus hívás (invokeDynamic): Ahelyett, hogy a régi típusú anonim osztályokat bytecode-ban hozná létre a fordítás során, a Java az
invokeDynamicutasítást használja. Ezt az utasítást a Java 7 vezette be a dinamikusan típusos nyelvek támogatására a JVM-en, és Java 8-ban a lambda kifejezések és metódusreferenciák megvalósítására szolgál. AzinvokeDynamickéslelteti a metódushívás feloldását futásidőben.
Mi történik "a motorháztető alatt":
Fordítási szakaszban:
- A fordító elemzi a lambda kifejezést.
- Meghatározza a lambda által hivatkozott funkcionális interfész típusát.
- Egy speciális szintetikus metódust (gyakran
lambda$prefixszel) generál ugyanabban a osztályban, amely tartalmazza a lambda logikáját. Ez a metódus ugyanazt a szignatúrát használja, mint a funkcionális interfész absztrakt metódusa. - A lambda használatának helyén a fordító az
invokeDynamicutasítást generálja. Ez az utasítás tartalmaz egy hivatkozást a bootstrap method-ra (LambdaMetafactory.metafactory).
Futásidőben:
- Amikor a JVM találkozik az
invokeDynamicutasítással, először meghívja a bootstrap method-ot (LambdaMetafactory.metafactory). - A bootstrap method dinamikusan generál egy call site-ot a memóriában.
- A call site létrehoz és visszaad egy példányt egy osztályból, amely megvalósítja a funkcionális interfészt. Ez a példány lehet:
- A helyszínen létrehozott (pl. egy generált
Constant CallSitesegítségével). - Gyorsítótárazott a későbbi használatra (stateless lambda esetén).
- Egy már létező példányra mutató referencia (pl. stateless lambda esetén, amely
Serializable-t implementál).
- A helyszínen létrehozott (pl. egy generált
- Ez a generált osztály delegálja a funkcionális interfész absztrakt metódusának hívását a generált szintetikus metódusra (
lambda$). - Az első hívás után a bootstrap method megjegyzi, melyik objektumot kell visszaadni, és a későbbi
invokeDynamichívások gyorsan elérhetik ezt az objektumot vagy közvetlenül hívhatják azt, anélkül, hogy újra meghívnák a bootstrap method-ot.
Így a lambda-k nem mindig hoznak létre új osztályt minden példányosításkor. Azok a lambda-k, amelyek nem fogják el a külső kontextusból származó változókat (stateless lambda-k), a funkcionális interfész implementációja lehet singleton vagy cache-elt példány, amely újrahasznosítható. A változókat fogó lambda-k (állapotos lambda-k) új példány létrehozását igénylik, amely tárolja a fogott változókat.
Az invokeDynamic használata lehetővé teszi a JIT-kompilátor számára, hogy hatékonyabban optimalizálja a funkcionális interfész metódushívásokat, mint a hagyományos anonim osztály modell, mivel a JIT tájékozottabb döntéseket hozhat futás közben.
// Funkcionális interfész példa
@FunctionalInterface
interface MyFunction {
String apply(String input);
}
public class LambdaExample {
public static void main(String[] args) {
// Lambda kifejezés használata
MyFunction func = s -> "Processed: " + s;
String result = func.apply("Test");
System.out.println(result);
}
}