2013-07-02 9 views
5

Ich habe eine Klasse, die AutoCloseable implementiert und mit dem neuen Try-with-resources-Konstrukt von Java 7 verwendet werden soll. Ich kann jedoch keine Möglichkeit finden, zu garantieren, dass der Benutzer meiner Klasse Try-with-Resources verwendet. Wenn dies nicht geschieht, kann meine Klasse sich nicht selbst schließen, und schlimme Dinge passieren. Gibt es einen Weg - Sprachkonstrukt oder anderes - um dies durchzusetzen? Sogar in der Lage zu erkennen, ob ich in einem try-with-resources-Block bin oder nicht, sodass ich eine Ausnahme auslösen kann, wenn das nicht der Fall ist, wäre gut (obwohl ein kompilierbares Konstrukt vorzuziehen wäre).Erzwinge try-with-resources Java 7

Danke!

+0

Was wollte der Benutzer verwalten, um Ihre Klasse manuell zu schließen anstatt automatisch mit einem Try-with-resources? Würde das Erstellen einer "finalize" -Methode, die 'close' genannt wird, Ihr Problem beheben? – Jeffrey

+0

Leider kann 'finalize' nicht garantiert werden. Der GC kann beschließen, ihn nicht anzurufen. – joshlf

+0

In der Tat, ich stelle mir vor, dass die Unzuverlässigkeit von "finalisieren" ist wahrscheinlich der Grund, dass Try-mit-Ressourcen eingeführt wurde. – joshlf

Antwort

3

Leider gibt es keine Möglichkeit, sich vor der Dummheit des Benutzers zu schützen.

Sie können die Methode finalize implementieren, um close aufzurufen; Auf diese Weise können Sie sicher sein, dass die Ressource mindestens geschlossen ist, wenn das Objekt als Garbage Collected erfasst wird, obwohl Sie nicht wissen können, wann (oder falls) dies geschieht.

Wenn Sie Einschränkungen bei der Verwendung Ihres Projekts vornehmen können, können Sie möglicherweise einige Richtlinien mithilfe der aspektorientierten Programmierung erzwingen, aber dann verwenden Sie Java nicht mehr.

+1

Die Verwendung von Object.finalize() wird in Java dringend empfohlen. –

+0

Es sei denn du weißt was du machst * und * hab keine andere Option;) – Joni

+2

@DavidHofmann: Ich denke du hast etwas falsch verstanden. Es ist wahrscheinlich (und aus gutem Grund) davon abgeraten, sich darauf zu verlassen, dass finalize vom GC aufgerufen wird, aber tatsächlich ist das * Implementieren * der Finalize-Methode in einigen Fällen die einzige Möglichkeit, Ressourcenlecks zu verhindern. – jarnbjo

-1

Nein, es gibt keinen Weg, gleiche schlechte Dinge passieren, wenn Sie Dateien oder Datenbankverbindungen halten öffnen und Sie sie nicht in der Nähe ...

Wenn die sensible Ressourcen genutzt und im Rahmen discarted werden können von einem Methodenaufruf können Sie jedes Mal öffnen/schließen, wenn Ihre API aufgerufen wird, wenn nicht, dann haben Sie einfach das Verhalten gut dokumentiert.

Ihre Klasse könnte auch einen Shutdown-Hook registrieren, damit Sie Ihre Ressourcen schließen können, wenn der jvm untergeht, aber ich sehe das sowieso als eine schlechte Übung für eine Bibliothek.

+1

Sie liegen falsch. Beide Dateien und Netzwerk- oder Datenbankverbindungen werden beim Finalisieren der jeweiligen Wrapper-Instanzen als letzte Möglichkeit zum Verhindern von Ressourcenlecks geschlossen. Es gibt keinen guten Grund, das gleiche Implementierungsmuster nicht für eigene Zwecke zu verwenden. – jarnbjo

+0

Danke @jarnbjo zur Klarstellung. Was ich jedoch im Sinn hatte ist, dass wenn Sie Ressourcen öffnen, ohne sie zu schließen, Sie ein Leck haben. Ich denke, es ist sowieso besser, das Leck früher als später zu sehen, für den Fall, dass Sie irgendwie Referenzen zu den nicht abgeschlossenen Objekten behalten, wird keine finalize-Methode aufgerufen. Wenn Sie sich auf GC verlassen, haben Sie möglicherweise ein nicht-deterministisches Verhalten, abhängig von der Menge an RAM- und GC-Einstellungen, die Ihr Programm ausführt –

1

Wenn Sie sich wirklich um Ressourcenverwaltung kümmern, dann ist die Verwendung eines Callback-Idioms der sicherste Ansatz. Sie setzen nicht die Sammlung, sondern eine API, die die Endbenutzer ermöglicht Elemente in der Auflistung zu handhaben:

public interface Callback<T> { 
    void handle(T item); 
} 

public class SuperVitalVault { 
    public void all(Callback<Precious> callback) { 
    try (...) { 
     for (Precious i : ...) { 
     callback.handle(i); 
     } 
    } 
    } 
} 

Sie können Callback.handle(T) ändern, um ein boolean zurück, wenn Sie ein vorzeitiges Ausscheiden unterstützen:

try (...) { 
     for (Precious i : ...) { 
     if (callback.handle(i)) break; 
     } 
    } 

Wenn Sie sich Ihre eigene Callback-Schnittstelle bleiben wollen von definieren, können Sie Guava die Verwendung Function<T, Boolean> oder Predicate<T>, aber beachten Sie, dass es die Semantik festgelegt durch Guava verletzt:

Instanzen von Function werden im Allgemeinen erwartet, dass sie referenziell transparent sind - keine Nebenwirkungen - und mit equals konsistent sein müssen, dh a.equals (b) impliziert, dass function.apply (a) .equals (function.apply() b)).

Instanzen von Prädikat werden im Allgemeinen als frei von Nebenwirkungen und als gleichwertig angesehen.