Java java.util.function.Supplier statt if-Statement?

mental.dIseASe

Lieutenant
Registriert
Dez. 2008
Beiträge
665
Hallo liebe Kollegen,

ich habe folgende Klasse:

Java:
public class MyClass {
    
    private final boolean retrieveItTheSpecialWay;
    
    public MyClass(final boolean retrieveItTheSpecialWay) {
        this.retrieveItTheSpecialWay = retrieveItTheSpecialWay;
    }
    
    public void doIt() {
        final Thing thingToUse = retrieveThingToUse();
        useIt(thingToUse);
    }
    
    private Thing retrieveThingToUse() {
        if (retrieveItTheSpecialWay) {
            return retrieveItTheSpecialWay();
        } else {
            return retrieveItTheRegularWay();
        }
    }
    
    private Thing retrieveItTheSpecialWay() {
        return //some supplying Logic
    }
    
    private Thing retrieveItTheRegularWay() {
        return // some different supplying logic
    }
    
    private void useIt(final Thing thingy) {
        
    }
}

Das heißt, ich habe eine Klasse, bei deren Instanziierung jeweils schon feststeht, wie sich retrieveThingToUse für dieses eine Objekt immer verhalten wird. Angenommen es wird jetzt vielfach doIt aufgerufen, dann ist es doch irgendwie doof, dass immer wieder die Prüfung gegen retrieveItTheSpecialWay ausgeführt wird. Ich vermute, dass man hier richtigerweise wohl eine Klassenhierarchie aufbauen würde, um das zu refactorn. Angenommen, man will diesen Schritt aber nicht gehen, wäre es dann nicht auch möglich, hier folgendes zu tun?

Java:
public class MyClass {
    
    private final Supplier<Thing> retriever;
    
    public MyClass(final boolean retrieveItTheSpecialWay) {
        this.retriever = retrieveItTheSpecialWay ? this::retrieveItTheSpecialWay : this::retrieveItTheRegularWay;
    }
    
    public void doIt() {
        final Thing thingToUse = retriever.get();
        useIt(thingToUse);
    }
    
    private Thing retrieveItTheSpecialWay() {
        return //some supplying Logic
    }
    
    private Thing retrieveItTheRegularWay() {
        return // some different supplying logic
    }
    
    private void useIt(final Thing thingy) {
        
    }
}

In dem Fall hätte ich die Abfrage gegen das Flag nur noch einmal bei der Instanziierung und nicht in jedem Aufruf von doIt. Meine Fragen lauten jetzt:

1. Bin ich damit tatsächlich schneller unterwegs oder gibt es bei Functional Interfaces irgendwelchen Overhead, den ich übersehe? (Ich bin übrigens zu faul zum Messen)
2. Optimiert die JVM sowas schon adaptiv für mich und ich kann mir das sparen, es also so lassen wie im ersten Code-Schnipsel?

Das Refactoring wird übrigens wohl den Aufbau einer Klassenhierarchie umfassen, aber mich interessieren diese Fragen aus theoretischer Perspektive.

zum Gruße
mental
 
In public void doIt() würde ich retrieveThingToUse() ternary inlinen.

Damit sollte das IF-Statement auch schneller sein als der retriever.get()-Call.
 
Niemanden interessiert, ob die eine Variante nun 0,1% schneller ist als die andere. Code muss lesbar sein! Optimieren kann man, wenn man tatsächlich Performanceprobleme feststellt.

Ansonsten hängt aber, wie du schon angemerkt hast, noch die JVM dazwischen. Die kann komplette if-else-Blöcke entfernen, wenn sie merkt, dass eh immer nur eine der beiden Varianten ausgeführt wird.
Dann kommt's natürlich auch noch darauf an, welche JVM konkret genutzt wird, und ob die JVM deinen Code als Hot oder Cold einstuft. Je nach JVM gibt's dann auch noch Limits bei der Anzahl der Methoden Parameter und der Methoden Länge, sodass es sein kann, dass dein Code niemals optimiert wird.

Das hier ist 'n sehr interessanter Talk über JVM und Performance Optimization und Edge Cases:
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: BeBur
Brauchst da gar nichts machen. Mit dem Konstrukt wird das automatisch inlined werden in der JVM.
Bool final im if
Guck dir einfach die Spotoptimierung an. ( kannste dir direkt zeigen lassen)
Sowas zu optimieren ist unnötig.
 
  • Gefällt mir
Reaktionen: abcddcba
Je nachdem wie das tatsächlich logisch zusammenhängt, würde ich das über die Hierarchie abfressen.

Dann gäbe es da zB eine abstrakte Methode fetch() in (einer) der Superklasse(n) und passende Implementierungen in den abgeleiteten.

Ergebnis, Code lesbar, logisch strukturiert und alles steht da, wo man es erwarten würde. Ansonsten müßtest Du mit Pech die ganze Chose noch paarmal reimplementieren, zuallererst statt mit bool TheWayImSupposedToDoThis per enum TheWayImSupposedToDoThis und einem switch drumrum, später dann mit mehr und mehr Implementierungen für Methoden, die Du an der Stelle niemals brauchen wirst.

Es hält ja nichts davon ab, die eine oder andere klasseninterne Methode zu haben, die Du dann aus der Klassenhierarchie aufrufen kannst und die gemeinsam in der jeweiligen Implementierung gekapselt werden. Da kommt es halt drauf an, in welcher Form sich die einzelnen Implementierungen konzeptuell ähneln oder unterscheiden.
 
Ich dachte mir schon, dass ich nicht versuchen sollte, der JVM vorzugreifen. Je nach Ausbaustufe wird das ganze dann in eine saubere Klassenhierarchie refactored. Danke für euren Input!
 
Zurück
Oben