【概要】
サブクラスの実装で finalize() メソッドがオーバーライドされており、その実装が誤ってスーパークラスの finalize() メソッドを呼び出し忘れてしまっても安全にスーパークラスのファイナライズ処理が行えるようにしたい。
サブクラスの実装で finalize() メソッドがオーバーライドされており、その実装が誤ってスーパークラスの finalize() メソッドを呼び出し忘れてしまっても安全にスーパークラスのファイナライズ処理が行えるようにしたい。
【キーワード】
ファイナライザ・ガーディアン、Finalizer Guardian、Finalize
【参考】
Effective Java プログラミング言語ガイド
【詳細】
ファイナライザ・ガーディアンとは、継承される可能性のあるクラスを定義する際に、サブクラスの実装によって finalize() メソッドが呼び出されなくなってしまう危険性を回避するためのものである。
ファイナライザ・ガーディアンとは、継承される可能性のあるクラスを定義する際に、サブクラスの実装によって finalize() メソッドが呼び出されなくなってしまう危険性を回避するためのものである。
例えば次のような Connection クラスがあるとする。
Connection クラスの finalize メソッド内では、 disconnect() が呼び出されなかった場合にも、インスタンスが GC される際に isConnected() が true であれば disconnect() が呼び出されるよう安全策がとられている。public class Connection { ... protected void finalize() throws Throwable { if (isConnected()) { disconnect(); } } ... }
このクラスを利用するサブクラスを実装する際、次の BadSubConnection クラスのように、Connection クラスで提供されている安全策を無効化してしまう危険性のある実装を行うことができてしまう。
releaseRelatedResources() メソッドの処理中に例外が発生した場合、 super.finalize() が呼び出されずに finalize() メソッドが終了してしまうため、Connection クラスで提供されている機構が機能しない。
もちろん、サブクラスの実装が次の GoodSubConnection のようにきちんと実装されていれば何の問題もない。public class BadSubConnection extends Connection { ... protected void finalize() throws Throwable { releaseRelatedResources(); super.finalize(); } ... }
しかし、どのように利用されようとも安定性を崩さないように実装するべきである。
ファイナライザ・ガーディアンを利用した Connection クラスの実装は次のようになる。public class GoodSubConnection extends Connection { ... protected void finalize() throws Throwable { try { releaseRelatedResources(); } finally { super.finalize(); } } ... }
Connection の finalize() メソッドを直接オーバーライドするのではなく、フィールド finalizerGuardian の finalize() メソッドをオーバーライドする。
このように実装しておくと、GoodSubConnection はもちろんのこと、 サブクラスの実装が仮に BadSubConnection のようになっていても finalizerGuardian の finalize() が必ず呼び出されるため、 変更前の Connection よりも安全に利用できるようになる。public class Connection { private final Object finalizerGuardian = new Object() { protected void finalize() throws Throwable { if (isConnected()) { disconnect(); } } } ... }
コメント