Ho un Altalena azione di classe, che funziona come segue:
package org.trypticon.hex.gui.datatransfer;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.FlavorListener;
import java.awt.event.ActionEvent;
import javax.annotation.Nonnull;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.TransferHandler;
import org.trypticon.hex.gui.Resources;
import org.trypticon.hex.gui.util.FinalizeGuardian;
import org.trypticon.hex.gui.util.FocusedComponentAction;
public class PasteAction extends FocusedComponentAction {
private final FlavorListener listener = (event) -> {
// this method in the superclass calls back `shouldBeEnabled`
updateEnabled();
};
@SuppressWarnings({"UnusedDeclaration"})
private final Object finalizeGuardian = new FinalizeGuardian(() -> {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.removeFlavorListener(listener);
});
public PasteAction() {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.addFlavorListener(listener);
}
@Override
protected boolean shouldBeEnabled(@Nonnull JComponent focusOwner) {
TransferHandler transferHandler = focusOwner.getTransferHandler();
if (transferHandler == null) {
return false;
}
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
DataFlavor[] flavorsInClipboard = clipboard.getAvailableDataFlavors();
return transferHandler.canImport(focusOwner, flavorsInClipboard);
}
@Override
protected void doAction(@Nonnull JComponent focusOwner) throws Exception {
Action action = TransferHandler.getPasteAction();
action.actionPerformed(new ActionEvent(
focusOwner, ActionEvent.ACTION_PERFORMED, (String) action.getValue(Action.NAME)));
}
}
Il FinalizeGuardian
di cui qui è attualmente implementato utilizzando finalize()
:
package org.trypticon.hex.gui.util;
public final class FinalizeGuardian {
private final Runnable cleanupLogic;
public FinalizeGuardian(Runnable cleanupLogic) {
this.cleanupLogic = cleanupLogic;
}
@Override
protected final void finalize() throws Throwable {
try {
cleanupLogic.run();
} finally {
super.finalize();
}
}
}
Così, per ovvi motivi, vorrei passare all'utilizzo di Cleaner
per questo.
Il primo tentativo è stato qualcosa di simile a questo:
package org.trypticon.hex.gui.util;
import java.lang.ref.Cleaner;
public final class FinalizeGuardian {
private static final Cleaner cleaner = Cleaner.create();
public FinalizeGuardian(Runnable cleanupLogic) {
cleaner.register(this, cleanupLogic);
}
}
Il problema è che ora l'oggetto non diventa mai phantom raggiungibile, perché:
Cleaner
di per sé ha un forte riferimento percleanupLogic
cleanupLogic
contiene un riferimento alistener
al fine di rimuovere l'ascoltatorelistener
contiene un riferimento all'azione di classe, in ordine di chiamataupdateEnabled
su di esso- l'azione di classe contiene un riferimento all'
FinalizeGuardian
in modo che non ottenere raccolti prematuramente
Perché il FinalizeGuardian
di per sé non diventa mai phantom raggiungibile, il pulitore non verrà mai chiamato.
Quindi quello che vorrei sapere è, c'è un modo per ristrutturare questo per seguire le regole necessarie per rendere Cleaner
lavoro, correttamente, che non comportano la rottura di incapsulamento spostando l'ascoltatore al di fuori della mia azione di classe?