2017-02-08 5 views
2

Betrachten Sie den folgenden Anwendungsfall einer Anwendung mit einem Benutzerpräferenzsystem:Optional ist `orElse` lazy evaluation scheitern führt zu Leistungseinbußen

Wir wollen MyFlag den Bool Wert der Vorzug erhalten.
Im besten Fall wollen wir es aus den Einstellungen des aktuellen Benutzers. Wenn das nicht funktioniert, möchten wir MyFlag von den Standardeinstellungen erhalten.
Wenn sogar das fehlschlägt, werfen.

Die Einstellungen sind auf einem Server. Diese Verbindung ist langsam und kann fehlschlagen.
Das Abrufen der Einstellungen und das Abrufen der Präferenz können ebenfalls fehlschlagen.
lässt So verwenden javas Optionals:

public static boolean getMyFlag()throws NoSuchElementException 
{ 
     return getUserOrDefaultPreference("MY_FLAG"); 
} 

private Boolean getUserOrDefaultPreference(String preferenceName) throws NoSuchElementException 
{ 
    return Optional.ofNullable(getConnection())    // get slow connection 
     .map(connection -> connection.getUserSettings())  // get user settings 
     .map(settings -> settings.getPref(preferenceName)) // return preference 
     .orElse(slowlyGetDefaultPreference(preferenceName)); // or else return default value 
} 

private Boolean slowlyGetDefaultPreference(String preferenceName) throws NoSuchElementException 
{ 
    return Optional.ofNullable(getConnection())    // get slow connection 
     .map(connection -> connection.getDefaultSettings()) // get default settings 
     .map(settings -> settings.getPref(preferenceName)) // return preference 
     .orElseThrow(() -> new NoSuchElementException()); // if any of the above fails throw 
} 

Problem hier ist, dass die Verbindung sehr langsam sein kann. Wenn .orElse(slowlyGet...); aufgerufen wird, wird die Funktion slowlyGetDefaultPreference() zuerst ausgewertet, unabhängig davon, ob mein Optional leer ist oder einen Wert hat. Dies ist eine Leistungseinbuße, die ich vermeiden muss.

Ich versuchte mit einem Lieferanten über .orElseGet(() -> slowlytGet..), aber das führte zu dem gleichen Problem.

So ist meine einzige Zuflucht ist das hässliche isPresent() anti-Muster, das aus einer Ablesbarkeit Sicht die gesamten Optional Strömungs ruiniert:

private Boolean getUserOrDefaultPreference(String preferenceName) throws NoSuchElementException 
{ 
    Optional<Boolean> opt = Optional.ofNullable(getConnection()) 
     .map(connection -> connection.getUserSettings()) 
     .map(settings -> settings.getPref(preferenceName)); 

    if(opt.isPresent()) 
    { 
     return opt.get(); 
    } 
    else 
    { 
     slowlyGetDefaultPreference(preferenceName)); 
    } 
} 

Das gleiche gilt die Ausnahme für das Werfen, wie ich will nicht das bezahlen Baukosten.

Fehle ich hier etwas oder ist das die einzige Lösung?

+0

'orElseGet' sollte genauso funktionieren wie das letzte Beispiel, ich verstehe nicht, warum Sie sagen, es hat das gleiche Problem wie 'orElse' – Magnus

+0

@Magnus Ich habe den Code mit dem Debugger durchgeblättert und konnte sehen, dass für' orElse' 'getSlow ..()' aufgerufen wurde. –

+1

Ja, weil die Auswertung von 'orElseGet' nur aufgerufen wird, wenn eine Funktion vor' orElseGet' ein Null-Objekt hat. Aber wenn Sie 'orElse' verwenden, heißt immer – MateuszW90

Antwort

1

Wenn ich getestet dieses druckt nur "smart":

public class SampleJava { 
    public static String stupid() { 
     System.out.println("stupid"); 
     return "stupid"; 
    } 
    public static void main(String[] args) { 
     System.out.println(Optional.ofNullable("smart").orElseGet(() -> stupid())); 
    } 
} 
3

Der Unterschied zwischen orElse() und orElseGet(lambdas) wird, wird die erste Funktion immer dann aufgerufen, aber orElseGet genannt wird, wenn einige obejct vor orElseGet in Optional.ofNullable() null war.

Es bedeutet, wenn Sie nicht jedes Mal Methode von orElse() anrufen möchten, Sie orElseGet()

In Ihrem Beispiel für die Methode verwenden, müssen slowlyGetDefaultPreference(preferenceName) bitte Code Sie orElseGet()

return Optional.ofNullable(getConnection())    // get slow connection 
    .map(connection -> connection.getUserSettings())  // get user settings 
    .map(settings -> settings.getPref(preferenceName)) // return preference 
    .orElseGet(() -> slowlyGetDefaultPreference(preferenceName)); 

dann nicht verwenden evaluate last method, wenn alle Objekt in map Funktion ist nicht null

+0

Sie haben Recht. Auf etwas dazwischen war in der Tat "null". Gott, dumm von mir. –

+0

Deshalb rufen Sie immer 'langsamGetDefaultPreference (PräferenzName)'. Freue mich zu helfen – MateuszW90

Verwandte Themen