2016-07-29 22 views
1

Ich denke, dass für die Erfassung von Lambdas, muss ein Objekt zugewiesen werden (sei es Object[] oder einige abc$Lambda$xyz Typ). Ist es möglich, diesen Prozess irgendwie anzupassen? Sagen wir, ich diesen Code haben:Pool Capturing Lambdas

private void test() { 
    int x = 5; 
    Supplier<Integer> supplier =() -> x; 
    foo(supplier); // potentially passes the supplier to another thread etc. 
} 

und ich will nicht das Objekt zuweisen x erfassen, sondern nur aus einem Pool bekommen und im Wert füllen; Ich weiß auch, dass ich irgendwann das Objekt in den Pool zurückbringen kann.

kann ich

Supplier<Integer> supplier = pool.get(x, v -> v); 

schreiben, und ich spezialisierte Versionen für unterschiedliche Argumenttypen (wie mit Object... die Zuteilung (ok, es gibt eine Chance haben könnte, dass die Zuteilung von Flucht beseitigt würde Analyse tun würde ...), aber das würde den Code ganz unlesbar machen. Deshalb für einen Aspekt artig ich bin auf der Suche.

Ist so etwas möglich?


EDIT: den Pool der Funktionalität deutlicher zu machen, werden die get könnte als

class IntHolderSupplier implements Supplier<Integer> { 
    int value; 
    IntFunction<Integer> func; 
    @Override public Integer get() { 
     return func.apply(value); 
    }   
} 

class Pool { 
    Supplier<Integer> get(int arg, IntFunction<Integer> func) { 
     IntHolderSupplier holder = ...; 
     holder.value = arg; 
     holder.func = func; 
     return holder; 
    } 
} 

umgesetzt und ich würde für alle möglichen Arten solcher Halter mit spezifischen Signaturen müssen lambdas ich verwenden möchte.

Vielleicht habe ich das Beispiel ein bisschen kompliziert, indem ich die Funktion zur Verfügung stellte - aber ich wollte die Tatsache erfassen, dass eine zusätzliche Berechnung auf das erfasste Argument zum Zeitpunkt Supplier.get() Invocation angewendet werden kann.

Und ignorieren Sie die Tatsache, dass die int eingerahmt wird, die eine Zuordnung produzieren.

+0

Meinst du etwas wie 'supplier =() -> pool.getInteger();'? – bradimus

+0

@bradimus Sorry, ich verstehe die Frage nicht. Die Absicht besteht darin, ein gepooltes Objekt zu erhalten, das bei seiner Geschäftslogik den Wert 5 zurückgibt. Der Pool kann diese Geschäftslogik nicht kennen. –

+0

Diese Halterimplementierungen sollten Ihnen helfen, die Erstellung von Objekten zu verhindern, wenn Function auch nicht erfasst wird. Ich bin sehr gespannt, wo Sie diesen Teil optimieren müssen, könnten Sie bitte den Kontext angeben, in dem er verwendet wird? –

Antwort

1

Um "Pool Capturing Lambdas" ist eine falsche Bezeichnung. Lambda-Ausdrücke sind eine technische Lösung, um eine Instanz einer funktionalen Schnittstelle zu erhalten. Da Sie den Lambda-Ausdrücke nicht bündeln, sondern die Schnittstelle Instanzen, jeden technischen Aspekt der Lambda-Ausdrücke, wie Unveränderlichkeit oder die Tatsache, dass die JRE/JVM steuert ihre Lebensdauer fallen, sollten Sie es „Pool funktionale Schnittstelle Instanzen“ nennen.

So können Sie einen Pool für diese Instanz implementieren, wie Sie einen Pool für jede Art von Objekt implementieren können. Es ist eher unwahrscheinlich, dass ein solcher Pool besser abschneidet als die JVM-verwalteten Objekte, die für Lambda-Ausdrücke erstellt wurden, aber Sie können es versuchen.

Es ist einfach, wenn man sie unveränderlich zu halten, also nicht versuchen, sie für einen anderen Wert wieder zu verwenden, aber nur dann, wenn wieder einen zuvor erfasste Wert zu stoßen. Hier ist ein Beispiel für einen Supplier Cache die Lieferanten für die letzten 100 angetroffen Werte halten:

class SupplierCache { 
    static final int SIZE = 100; 
    static LinkedHashMap<Object,Supplier<Object>> CACHE = 
     new LinkedHashMap<Object, Supplier<Object>>(SIZE, 1f, true) { 
     @Override 
     protected boolean removeEldestEntry(Map.Entry<Object, Supplier<Object>> eldest) { 
      return size() > SIZE; 
     } 
    }; 
    @SuppressWarnings("unchecked") 
    static <T> Supplier<T> getSupplier(T t) { 
     return (Supplier<T>)CACHE.computeIfAbsent(t, key ->() -> key); 
    } 
} 

(Thread-Sicherheit hinzufügen, wenn Sie es brauchen). So durch Supplier<Integer> supplier =() -> x; mit Supplier<Integer> supplier = SupplierCache.getSupplier(x); ersetzt werden Sie die Cache-Funktionalität erhalten und da Sie müssen sie nicht loslassen, Sie haben keine fehleranfällige Annahmen über seinen Lebenszyklus zu machen.

einen Pool von Objekten Erstellen Supplier und die Rückgabe des Werts eines veränderlichen Feld Implementierung, so dass Sie manuell Instanzen zurückfordern können, ist nicht allzu schwer, wenn Sie einfach eine normale Klasse erstellen Supplier Umsetzung, aber gut, öffnen Sie ein Ganze von Würmern mit manueller Speicherverwaltung, einschließlich des Risikos, ein noch in Benutzung befindliches Objekt zurückzugewinnen. Diese Objekte können nicht wie im obigen Beispiel wie das unveränderliche Objekt geteilt werden. Und Sie ersetzen die Objektzuordnung durch die Suche nach einer wiederherstellbaren Pool-Instanz und die explizite Zurücksetzung einer Instanz nach der Verwendung. Es gibt keinen Grund, warum dies schneller sein sollte.

+0

Das Ersetzen von '() -> x' mit' SupplierCache.getSupplier (x) 'ist genau das, was ich vermeiden wollte. –

+0

Nun, wenn Sie die Quellcodeform der Lambda-Ausdrücke beibehalten möchten, gibt es keine integrierte Lösung, wie erklärt, aufgrund des fraglichen Ergebnisses. Es wäre jedoch möglich, sie auf kompilierten Klassendateien zu implementieren.Im Allgemeinen ist die JRE zuständig. Wenn Sie also ein anderes Verhalten einfügen möchten, ohne die JRE zu ändern, müssen Sie die Bootstrap-Methode mithilfe von Bytecode-Manipulation/Instrumentation auf eine andere Implementierung mit dieser Caching-Funktion umleiten. – Holger

+0

@Holger der nette Trick über LRU Cache .. definitiv ein Plus! – Eugene