CodeGym /Kurse /Spring Core /Optional-Abhängigkeiten in Spring

Optional-Abhängigkeiten in Spring

Spring Core
Level 9, Lektion 0
Verfügbar

1. Verpflichtende und fehlende Beans

Wenn Sie an reines Java gewöhnt sind, wirkt der Gedanke „Die Anwendung wurde nicht gestartet, weil irgendeine Abhängigkeit nicht gefunden wurde“ zunächst etwas dramatisch. Im normalen Code können Sie schließlich new schreiben, und alles funktioniert. Aber in Spring ist die Idee eine andere: Der Container ist Ihr strenger Kollege, der zu Beginn überprüft, dass der Objektgraph prinzipiell zusammensetzbar ist und nicht irgendwo mitten im Szenario auseinanderfällt.

Stellen wir uns vor, wir haben im Projekt eine „Diagnostik der Auftragserstellung“ hinzugefügt. Das ist kein Geschäfts-Audit, das Teil der Domäne ist, sondern einfach eine technische „Hervorhebung, dass etwas passiert ist“. Wenn der OrderPlacementService diese Diagnostik als verpflichtende Abhängigkeit benötigt und der Bean nicht registriert wurde, wird Spring sofort fehlschlagen – und das ist ehrlich.

import org.springframework.stereotype.Service;

@Service
public class OrderPlacementService {
    // Verpflichtende Abhängigkeit: Fehlt der Bean, fällt der Container ehrlich zu Beginn aus.
    private final OrderDiagnostics diagnostics;

    public OrderPlacementService(OrderDiagnostics diagnostics) {
        this.diagnostics = diagnostics;
    }
}

Wenn OrderDiagnostics nirgendwo als Bean registriert ist, sehen Sie zu Beginn das bekannte „Containermurren“ in Form von NoSuchBeanDefinitionException. Im Normalfall ist das gut, denn die Abhängigkeit war verpflichtend: Der Dienst kann ohne sie nicht wie geplant arbeiten.

Das Problem beginnt, wenn wir sagen: „Hier ist das Fehlen einer Abhängigkeit kein Fehler. Es ist Teil des Designs.“ Zum Beispiel ist die Diagnostik eine nette Sache, aber der Auftrag wird auch ohne sie erstellt. Und dann müssen wir dem Container ehrlich sagen können: „Falle nicht aus. Diese Abhängigkeit ist optional."

2. Optional-Abhängigkeit ohne „Optional-Suppe“

Das Wort optional provoziert im Code oft den Gedanken: „Super! Man kann alles optional machen, und nichts wird jemals fehlschlagen.“ Das ist etwa so, als ob man das Türschloss entfernt, um die Schlüssel nicht zu verlieren. Bequem … bis es sehr interessant wird.

Optional-Abhängigkeit ist eine Abhängigkeit, deren Fehlen den Sinn der Klasse nicht beeinträchtigt. Die Klasse bleibt also nützlich und vorhersagbar, funktioniert nur in einem „reduzierten Modus“. Ein gutes mentales Modell: nicht „Ich habe keine Lust, das Wiring zu reparieren“, sondern „Ich entwerfe eine Erweiterung / ein Plugin“.

In ContextFlow ist ein typisches Beispiel für eine optional-Mechanik eine Art Überbau, der das Szenario „verbessert“, aber nicht sein Kern ist. Zum Beispiel könnten wir zusätzliche technische Diagnostik wünschen, um im Dev-Modus etwas mehr Informationen in der Konsole zu sehen. Ohne sie wird der Auftrag erstellt, der Rabatt berechnet, die Benachrichtigung gesendet – alles nach Plan.

Hier ist es wichtig, eine optional-Abhängigkeit nicht mit „verpflichtend, aber ich weiß nicht, wie ich sie registrieren soll“ zu verwechseln. Wenn Ihr Dienst ohne die Abhängigkeit zu einer leeren Hülle wird, die nichts tut oder etwas falsch macht, dann ist es keine Flexibilität, sondern ein stiller Fehler, sie optional zu machen. In solchen Fällen ist es besser, der Container fällt aus. Ein Ausfall zu Beginn ist, so seltsam es klingt, eine der freundlichsten Dinge, die Spring für Ihre Zukunft tun kann.

3. Optional<T>: Fehlen eines Beans als Szenario

Optional<T> ist vielen noch aus Java Core bekannt, aber in Spring erhält es eine sehr praktische Bedeutung: Der Container kann Optional.empty() injizieren, wenn ein Bean vom Typ T fehlt. Und umgekehrt – wenn es genau einen Bean gibt, gelangt er in das Optional.

Um das Beispiel möglichst „innerhalb des Projekts“ zu halten, führen wir einen kleinen Port im Stil von ContextFlow ein. Dies ist kein verpflichtender Teil der Domäne, sondern ein sauberer technischer „Hook“.

// src/main/java/com/example/contextflow/domain/ports/OrderDiagnostics.java
// Port: Die Implementierung kann als optionales Plugin angeschlossen werden (Bean kann fehlen).
public interface OrderDiagnostics {
    void orderCreated(String orderId);
}

Jetzt können wir es im Dienst als Optional<OrderDiagnostics> injizieren:

import java.util.Optional;
import org.springframework.stereotype.Service;

@Service
public class OrderDiagnosticsSupport {
    // Optional im Typ zeigt explizit: Die Abhängigkeit kann fehlen (und das ist in Ordnung).
    private final Optional<OrderDiagnostics> diagnostics;

    public OrderDiagnosticsSupport(Optional<OrderDiagnostics> diagnostics) {
        this.diagnostics = diagnostics;
    }

    public void onCreated(String orderId) {
        // Sichere Nutzung: Fehlt der Bean, tun wir einfach nichts.
        diagnostics.ifPresent(d -> d.orderCreated(orderId));
    }
}

Hier sind sofort zwei Vorteile zu sehen. Erstens ist der optional-Charakter der Abhängigkeit in der Signatur des Konstruktors vermerkt. Zweitens erfolgt die Behandlung des Fehlens direkt bei der Nutzung – über ifPresent, nicht über chaotische if (x != null) überall in der Klasse.

Bewahren Sie Optional nicht im Feld auf

In Java gibt es einen populären Gedanken: „Optional ist nicht für Felder gedacht.“ Er ist nicht in Stein gemeißelt, aber die Intuition ist nützlich: Meistens möchte man Optional nicht durch die ganze Klasse schleppen. Im DI-Code ist es praktisch, es so zu machen: Optional<T> im Konstruktor entgegennehmen, aber intern die normale Abhängigkeit speichern, jedoch mit einem sicheren Fallback.

Zum Beispiel erstellen wir eine kleine No-Op-Variante:

public class NoOpOrderDiagnostics implements OrderDiagnostics {
    @Override
    public void orderCreated(String orderId) {
        // intentionally no-op
    }
}

Und verwenden sie einmal im Konstruktor:

import java.util.Optional;
import org.springframework.stereotype.Service;

@Service
public class OrderDiagnosticsSupport {
    private final OrderDiagnostics diagnostics;

    public OrderDiagnosticsSupport(Optional<OrderDiagnostics> diagnostics) {
        // Einmalige Normalisierung: Danach ist das Feld immer non-null (entweder echte Implementierung oder no-op).
        this.diagnostics = diagnostics.orElseGet(NoOpOrderDiagnostics::new);
    }

    public void onCreated(String orderId) {
        diagnostics.orderCreated(orderId); // sicher: entweder echte Diagnostik oder no-op
    }
}

So wird der Code sehr „glatt“: Keine ifPresent() in jeder Methode – einmal das Verhalten bei Fehlen festgelegt und dann beruhigt weiterleben.

Optional löst keine Mehrdeutigkeit

Ein sehr häufiger Anfängerfehler: „Wenn ich die Abhängigkeit in Optional verpacke, hört Spring auf zu meckern.“ Nein. Es hört nur beim Fehlen des Beans auf zu meckern. Wenn es mehrere Beans gibt, tritt dieselbe Mehrdeutigkeit auf wie zuvor.

Das heißt, Optional<NotificationSender> wird bei drei Benachrichtigungs-Sendern keine magische Auswahl treffen. Der Container weiß immer noch nicht, welchen NotificationSender Sie gemeint haben. Das ist wichtig zu merken, weil man sonst leicht eine seltsame Erwartung bekommt: „Na, optional … wähle doch irgendeinen.“ Spring funktioniert nicht so (und ehrlich gesagt – danke dafür).

Optional und @Qualifier

Manchmal wird eine optional-Abhängigkeit nicht für „irgendeine Implementierung“, sondern für eine bestimmte benötigt. Wir möchten zum Beispiel eine zusätzliche SMS-Benachrichtigung senden, aber nur, wenn der SMS-Kanal als Bean angeschlossen ist. Dann macht @Qualifier die Auswahl explizit, und Optional erlaubt das Fehlen.

import java.util.Optional;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class SmsMarketingService {
    // Selbst mit Qualifier bleibt die Abhängigkeit optional: Der spezifische Kanal kann nicht angeschlossen sein.
    private final Optional<NotificationSender> smsSender;

    public SmsMarketingService(
            @Qualifier("smsNotificationSender") Optional<NotificationSender> smsSender
    ) {
        this.smsSender = smsSender;
    }

    public void sendPromo(String text) {
        // Fehlt der SMS-Bean, überspringen wir einfach diesen Schritt, das Hauptszenario wird nicht gebrochen.
        smsSender.ifPresent(s -> s.send(text));
    }
}

Der Sinn hier ist gut: „Fehlt die SMS-Integration im Build, überspringen wir diesen Schritt einfach.“ Und das bricht das Hauptszenario ContextFlow nicht.

4. @Nullable: Schnell, aber mit dem Preis von null

Nach Optional<T> fragt man sich logischerweise: „Geht es noch kürzer?“ Ja, aber der Preis ist der klassische Java-Preis: null. In Spring können Sie den Injection-Point als nullable markieren, und wenn der Bean fehlt, injiziert der Container null, ohne zu Beginn zu scheitern.

Wichtig: In diesem Thema ist fast immer org.springframework.lang.Nullable gemeint. Das ist eine Spring-eigene Annotation, kein zufälliges @Nullable aus einem Drittanbieter-Paket, das die IDE Ihnen hilfreich in der Autovervollständigung anbietet.

import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;

@Service
public class OrderDiagnosticsSupport {
    private final OrderDiagnostics diagnostics;

    public OrderDiagnosticsSupport(@Nullable OrderDiagnostics diagnostics) {
        // Der Container kann null übergeben, wenn der Bean fehlt.
        this.diagnostics = diagnostics;
    }

    public void onCreated(String orderId) {
        // Mit null muss man diszipliniert sein: Vor jeder Nutzung prüfen.
        if (diagnostics != null) {
            diagnostics.orderCreated(orderId);
        }
    }
}

Der Code ist kürzer? Ein wenig. Aber er hat das typische Problem: Sie müssen sich an null an jeder Verwendungsstelle erinnern, sonst bekommen Sie irgendwann eine NullPointerException an einer unerwarteten Stelle, und Ihr „optionales“ Design wird zu einer „optionalen Stabilität“.

Normalisierung von @Nullable

Der Trick ist derselbe wie bei Optional: Besser einmal die Abhängigkeit im Konstruktor normalisieren und dann ein non-null Feld speichern.

import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;

@Service
public class OrderDiagnosticsSupport {
    private final OrderDiagnostics diagnostics;

    public OrderDiagnosticsSupport(@Nullable OrderDiagnostics diagnostics) {
        // Null einmal in no-op normalisieren – und dann ohne NPE leben.
        this.diagnostics = (diagnostics != null) ? diagnostics : orderId -> {};
    }

    public void onCreated(String orderId) {
        diagnostics.orderCreated(orderId); // NPE wird nicht mehr auftreten
    }
}

Wir verwenden ein Lambda als No-Op-Implementierung. Das ist in Ordnung, weil OrderDiagnostics ein funktionales Interface ist (eine Methode). Falls Ihnen das wohler ist, können Sie eine separate NoOp...-Klasse erstellen – das ist ausführlicher, aber für das Team besser lesbar.

Wo ist @Nullable geeignet

Im Studienprojekt und in den meisten anwendungsorientierten Diensten ist Optional in der Regel besser lesbar. Aber @Nullable kommt auch in der Praxis vor, besonders wenn Sie mit Code integrieren, der bereits um null herum aufgebaut ist, oder wenn es Ihnen wichtig ist, Optional nicht in Felder/Datenstrukturen zu ziehen. In jedem Fall ist es eine Wahl: „Ich erlaube bewusst null“, nicht „na ja, ist halt kürzer“.

Und noch einmal: @Nullable löst nicht das Problem mehrerer Kandidaten. Gibt es mehrere Beans, kommt es zu Mehrdeutigkeit. Nullable beeinflusst nur das Szenario „Bean fehlt“.

5. ObjectProvider<T>: Zugriff auf den Bean „bei Bedarf“

ObjectProvider<T> ist ein Spring-Werkzeug, das etwas „containerhafter“ wirkt als Optional. Sie injizieren nicht die Abhängigkeit selbst, sondern einen Provider, und aus diesem holen Sie den Bean dann, wenn Sie ihn benötigen. Gefühlt erinnert das an einen „Griff“ an den Container, aber viel sauberer, als den ApplicationContext in den Dienst zu ziehen (das machen wir heute nicht).

Gleich eine wichtige Klarstellung, damit wir nicht in die nächste Vorlesung abweichen: ObjectProvider steuert das Holen der Referenz, nicht die zwingende „lazy creation“. Die Frage „Wann den Bean erzeugen“ werden wir separat besprechen. Jetzt liegt der Fokus darauf, dass die Abhängigkeit fehlen kann und man das schön behandeln muss.

Beispiel mit unserem OrderDiagnostics:

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;

@Service
public class OrderDiagnosticsSupport {
    // Provider injizieren, nicht den Bean selbst: Entscheidung „nehmen oder nicht“ im Moment der Nutzung.
    private final ObjectProvider<OrderDiagnostics> diagnosticsProvider;

    public OrderDiagnosticsSupport(ObjectProvider<OrderDiagnostics> diagnosticsProvider) {
        this.diagnosticsProvider = diagnosticsProvider;
    }

    public void onCreated(String orderId) {
        // Fehlt der Bean, erhalten wir null (im Unterschied zu Optional.empty()).
        OrderDiagnostics diagnostics = diagnosticsProvider.getIfAvailable();
        if (diagnostics != null) {
            diagnostics.orderCreated(orderId);
        }
    }
}

Die Methode getIfAvailable() tut genau das, was auf der Verpackung steht: Sie gibt den Bean zurück, wenn er existiert, sonst null. Das heißt, ObjectProvider im „optionalen Modus“ bringt uns wieder zu null, gibt dafür aber zusätzliche Flexibilität: Sie können entscheiden, wann Sie diesen Bean überhaupt anfassen wollen.

getIfAvailable() mit Fallback

ObjectProvider hat eine Überladung, die es erlaubt, einen Fallback-Lieferanten anzugeben. Das hilft, null im restlichen Code zu vermeiden.

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;

@Service
public class OrderDiagnosticsSupport {
    private final ObjectProvider<OrderDiagnostics> diagnosticsProvider;

    public OrderDiagnosticsSupport(ObjectProvider<OrderDiagnostics> diagnosticsProvider) {
        this.diagnosticsProvider = diagnosticsProvider;
    }

    public void onCreated(String orderId) {
        OrderDiagnostics diagnostics =
                diagnosticsProvider.getIfAvailable(() -> id -> {});
        diagnostics.orderCreated(orderId);
    }
}

Ja, es sieht etwas aus wie eine „Lambda-Matrjoschka“, aber die Idee ist einfach: Wenn der Bean existiert, verwenden wir ihn; wenn nicht, nehmen wir den No-Op und machen weiter.

ObjectProvider innerhalb einer Methode

Mit Optional treffen Sie die Entscheidung meist im Konstruktor: vorhanden/nicht vorhanden, und der Dienst lebt damit. Mit ObjectProvider können Sie die Entscheidung „vor Ort“ treffen. Zum Beispiel die Diagnostik nur für „große“ Aufträge einschalten (das ist nur eine Demonstration, keine Jahrhundert-Geschäftsregel):

import java.math.BigDecimal;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;

@Service
public class LargeOrderDiagnostics {
    private final ObjectProvider<OrderDiagnostics> diagnosticsProvider;

    public LargeOrderDiagnostics(ObjectProvider<OrderDiagnostics> diagnosticsProvider) {
        this.diagnosticsProvider = diagnosticsProvider;
    }

    public void maybeLog(BigDecimal total, String orderId) {
        // Bis zu einer bestimmten Schwelle wird die Diagnostik gar nicht benötigt – wir rühren den Provider nicht an.
        if (total.compareTo(BigDecimal.valueOf(10_000)) < 0) {
            return;
        }
        // Den optionalen Bean nur auf dem „seltenen“ Ausführungspfad holen.
        OrderDiagnostics d = diagnosticsProvider.getIfAvailable();
        if (d != null) d.orderCreated(orderId);
    }
}

Hier ist die optional-Entscheidung direkt innerhalb des Zweigs lokalisiert. Das ist praktisch, wenn die optionale Abhängigkeit selten benötigt wird und Sie nicht möchten, dass ihre Anwesenheit den Haupt-Happy-Path durch das Lesen von Optional „verschmutzt“.

getIfUnique() bei Mehrdeutigkeit

Wie Optional löst auch der Provider nicht das Problem „es gibt mehrere Beans“. Aber er gibt Ihnen die Methode getIfUnique(): Sie gibt den Bean nur zurück, wenn es genau einen gibt, sonst gibt sie null zurück. Manchmal ist das als „sanfte“ Strategie nützlich: Können wir nicht eindeutig wählen, wählen wir gar nicht.

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;

@Service
public class DefaultNotificationInspector {
    private final ObjectProvider<NotificationSender> senderProvider;

    public DefaultNotificationInspector(ObjectProvider<NotificationSender> senderProvider) {
        this.senderProvider = senderProvider;
    }

    public void printIfUnique() {
        NotificationSender sender = senderProvider.getIfUnique();
        System.out.println(sender == null ? "not unique" : sender.getClass().getSimpleName());
        // not unique
    }
}

In unserem Projekt mit mehreren NotificationSender wird das fast immer not unique sein, und das ist in Ordnung: Dort muss die Auswahl explizit sein (über @Qualifier, @Primary, Map<String, T>). Aber als Verständnismechanismus ist getIfUnique() nützlich.

6. Spickzettel: Optional, @Nullable, ObjectProvider

Wenn Sie zum ersten Mal drei ähnliche Werkzeuge sehen, möchten Sie fragen: „Welches ist das richtige?“ Die Antwort ist im Erwachsenenstil langweilig: Das richtige ist dasjenige, das den Sinn am genauesten ausdrückt. Aber um es einfacher zu machen, hier eine kurze Tabelle, und dann verlassen Sie sich auf den gesunden Menschenverstand und die Lesbarkeit.

Werkzeug Was erhalten Sie im Konstruktor Was passiert, wenn der Bean fehlt Wo das Fehlen am besten behandelt wird Stärke
Optional<T> Optional<T> Optional.empty() Im Konstruktor (via orElse...) oder direkt beim Aufruf (ifPresent) Die „selbstdokumentierendste“ Variante
@Nullable T T null Fast immer im Konstruktor (mit Umwandlung in no-op) Minimaler Syntaxaufwand, aber Risiko von null
ObjectProvider<T> Provider getIfAvailable() gibt null zurück Innerhalb der Methode, „bei Bedarf“ Kontrolliertes Holen der Abhängigkeit an der gewünschten Stelle

Um das mentale Modell zu festigen, hier ein kleines Entscheidungsdiagramm. Es geht nicht um „was üblich ist“, sondern um „was Sie mit Code ausdrücken möchten“:

flowchart TD
    A["Benötigt Abhängigkeit T"] --> B{"Bleibt die Klasse ohne T sinnvoll?"}
    B -->|nein| C["Normale Abhängigkeit im Konstruktor (required)"]
    B -->|ja| D{"Möchten Sie, dass das Optionale in den Typen sichtbar ist?"}
    D -->|ja| E["Optional<T>"]
    D -->|nein| F{"Bereit, mit null zu leben und es zu kontrollieren?"}
    F -->|ja| G["@Nullable T (besser im Konstruktor normalisieren)"]
    F -->|nein| H["ObjectProvider<T> und getIfAvailable() + fallback"]

Und zum Schluss eine sehr bodenständige Anmerkung zu ContextFlow. In unserem Studienprojekt sollten Sie optional-Abhängigkeiten am besten als optionale Erweiterungen betrachten, nicht als eine Möglichkeit, „die Hälfte der Anwendung auszuschalten“. Wenn sich ohne die Abhängigkeit das geschäftliche Szenario grundlegend ändert, brauchen Sie in den meisten Fällen keine optional-Abhängigkeit, sondern eine explizite Strategie (z. B. NoOp... als normaler Bean) oder eine saubere Trennung der Konfigurationen. Für ein Basisprojekt reicht diese Unterscheidung bereits: Eine optional-Abhängigkeit ist eine Erweiterung, keine Möglichkeit, einen verpflichtenden Teil des Szenarios stillschweigend auszuschalten.

7. Typische Fehler

Fehler Nr. 1: Eine verpflichtende Abhängigkeit optional machen, damit es „einfach läuft“.
Das ist der beliebteste „Anti-Refactoring“. Der Container ist ausgefallen – und anstatt zu fragen „Warum sollte der Dienst ohne dieses Teil nicht leben?“, wickelt der Entwickler sie in Optional. In der Folge startet die Anwendung, aber das Schlüsselszenario kann stillschweigend kaputt sein, ohne Fehler beim Start. Wenn die Abhängigkeit für den Sinn der Klasse wirklich verpflichtend ist, lassen Sie Spring ruhig ausfallen – das ist billiger, als später Bugs zu fangen.

Fehler Nr. 2: Optional verwenden, aber trotzdem optional.get() schreiben.
Optional.get() ohne Prüfung ist ungefähr so, wie ein Trinkglas in einem Safe vor Stößen aufzubewahren. Formal haben Sie etwas getan, aber der Sinn ist verloren. Wenn Sie eine optional-Abhängigkeit injizieren, dann entweder ifPresent oder einmal im Konstruktor orElse(...) und dann mit einer normalen non-null-Referenz arbeiten.

Fehler Nr. 3: Mehrere optional-Modelle für eine Abhängigkeit mischen.
Manchmal sieht man ein „Kombi“: Optional<T> und gleichzeitig @Nullable (oder noch ObjectProvider daneben). Das macht den Vertrag fast immer unklar: Es ist nicht verständlich, was der Autor sagen wollte. Wählen Sie ein Modell, machen Sie es lesbar, und zwingen Sie den zukünftigen Leser nicht, Ihre architektonische Absicht wie ein Rätsel zu erraten.

Fehler Nr. 4: Das Fehlen einer Abhängigkeit in der gesamten Klasse behandeln, indem man Prüfungen verteilt.
Wenn in jeder Methode zweimal if (dep != null) oder if (opt.isPresent()) vorkommt, wird die Klasse schnell zu einem „Roman darüber, was wir nicht haben“. Viel gesünder ist es, die Abhängigkeit einmal im Konstruktor zu normalisieren (zu no-op) und dann normalen Code ohne Verzweigungen zu schreiben.

Fehler Nr. 5: Erwarten, dass die optional-Injektion die Mehrdeutigkeit der Kandidaten löst.
Optional<NotificationSender> wählt Ihnen keinen „irgendeinen Sender“ aus. Wenn es mehrere Kandidaten gibt, wird der Container genauso meckern wie ohne Optional. Optional löst nur das Szenario „null Kandidaten“. Wenn es viele Kandidaten gibt, kehren wir zu den Werkzeugen des vorherigen Tages zurück: @Qualifier, @Primary, Map<String, T>.

1
Aufgabe
Spring Core, Level 9, Lektion 0
Gesperrt
Optionale Telemetrie des Berichts über Optional
Optionale Telemetrie des Berichts über Optional
1
Aufgabe
Spring Core, Level 9, Lektion 0
Gesperrt
Nullable-Überschrift und Signatur bei Bedarf
Nullable-Überschrift und Signatur bei Bedarf
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION