package java.lang.ref; import java.util.Objects; import java.util.function.Consumer; /** *

Finalizator provides an alternative form of finalization which can be more * efficiently combined with (almost) regular manual cleanup where it serves as * a last-resort cleanup mechanism when manual cleanup is not performed. *

* Instead of overriding the Object's {@link #finalize()} method, a class * arranges in it's constructor(s) to {@link #create(Object, Consumer) create} a * {@code Finalizator} object which is used by GC to track the reachability of * given {@code finalizee} and invoke given {@code thunk} with it when such * {@code finalizee} becomes unreachable. *

* {@code Finalizator} can be {@link #clean() invoked} manually by user code that * decides to perform cleanup even before GC invokes it. The 1st invocation of * {@link #clean()} method performs the cleanup by invoking the {@code thunk}, * subsequent invocations are ignored. *

* After the cleanup is performed, the * {@code Finalizator} is {@link #clear() cleared} which breaks the links between: *

* In the absence of other links, finalizator, finalizee and thunk become * unreachable and eligible for GC. *

* Here's an example of a classic finalizable class: *

{@code
 *      public class Classic {
 *          @Override
 *          protected void finalize() {
 *              // clean-up actions invoked at most once...
 *          }
 *      }
 * }
*

* And this is an alternative using {@code Finalizator}, combining finalization * with manual cleanup: *

{@code
 *      public class Alternative {
 *          private final Finalizator finalizator =
 *              Finalizator.create(this, Alternative::cleanup);
 *
 *          void cleanup() {
 *              // clean-up actions invoked at most once...
 *          }
 *
 *          // manually triggered cleanup
 *          public void close() {
 *              finalizator.run();
 *          }
 *      }
 * }
* * @param the type of finalizee tracked by Finalizator * @since 1.9 */ public final class Finalizator extends Finalizer implements Cleaner { private Consumer thunk; /** * Creates and returns an instance of Finalizator used by GC to track given * {@code finalizee} and invoke given {@code thunk} with it when the * finalizee becomes unreachable. * * @param finalizee the instance to track it's reachability * @param thunk a {@link Consumer} which is invoked and passed the * {@code finalizee} when it becomes unreachable. * @param the type of {@code finalizee} * @return a Finalizator used by GC to track given * {@code finalizee} and invoke given {@code thunk} with it when the * finalizee becomes unreachable * @throws NullPointerException if either {@code finalizee} of {@code thunk} * are null */ public static Finalizator create(T finalizee, Consumer thunk) { Objects.requireNonNull(finalizee); Objects.requireNonNull(thunk); Finalizator finalizator = new Finalizator<>(finalizee, thunk); finalizator.link(); return finalizator; } private Finalizator(T finalizee, Consumer thunk) { super(finalizee); this.thunk = thunk; } /** * Invoked by GC when the tracked finalizee becomes unreachable or * by user code at any time. * It invokes the finalizator's thunk with the finalizee if this * Finalizator has not been {@link #clear() cleared} before that. If invoked * multiple times, only the 1st invocation results in the invocation * of the thunk. The finalizator releases the reference to the thunk and * finalizee upon 1st invocation of this method regardless of whether * it was performed by GC or by user code. */ @Override public void clean() { super.clean(); } /** * Invoke the {@link #thunk} passing the {@code finalizee} to it. */ @Override void invokeFinalizee(T finalizee) throws Throwable { Consumer thunk = this.thunk; this.thunk = null; thunk.accept(finalizee); finalizee = null; } /** * Returns {@code null} to prevent unwanted retention of the tracked * {@code finalizee}. * * @return {@code null} */ @Override public T get() { return null; } /** * Finalizator is not registered with a reference queue when created, * so this method always returns {@code false}. * * @return {@code false} */ @Override public boolean isEnqueued() { return false; } /** * Finalizator is not registered with a reference queue when created, * so this method does nothing and always returns {@code false}. * * @return {@code false} */ @Override public boolean enqueue() { return false; } /** * Clears this Finalizator and releases it's tracked {@code finalizee} and * it's {@code thunk} if they have not been released yet. * Invoking this method does not invoke the Finalizator's {@code thunk}. * It prevents from {@link #clean() running} the {@code thunk} in the future * if it has not been run yet. */ @Override public void clear() { super.clear(); } }