いい具合にトラブってしまったのでメモです。
クラスの定義にはいくつかの種類がありますが、あるクラスの内部にネストした(入れ子になった)クラスを定義することができます。このとき、修飾子として static をつけたネストしたクラスと、それがないネストしたクラスのどちらも正しい記述です。
public class OuterClass {
// static でないネストしたクラス
public class InnerClass {
}
// static なネストしたクラス
public static class StaticInnerClass {
}
}
static の有無で何が違うかといえば、static でない方は暗黙的に外側のクラス(上記の例ではOuterClass)への参照を持つため、外側のクラスのメンバにアクセスできるという点があります。
ここで、例外を何かのクラスにネストしたクラスとして定義してみたとすると、直列化(シリアライズ)で問題が起こります。
public class OuterClass {
// static でないネストしたクラスとして例外を定義
public class BadException extends RuntimeException {}
}
暗黙のうちにもつ外側のクラスへの参照は transient ではないので、シリアライズの対象になります。例外はSerializableを実装しているので直列化ができますが、このBadExceptionを直列化すると外側のクラスへの参照もシリアライズの対象となるものの、肝心のOuterClassはSerializableを実装していないので、直列化できずに例外が投げられます。
暗黙の内に持つ外側のクラスへの参照が引き起こす問題は、メモリリークの文脈で語られることが多いように思いますが、このように直列化でも問題となります。
public class OuterClass {
// static なネストしたクラスとして例外を定義
public static class MyException extends RuntimeException {}
}
例外にかぎらず、ネストしたクラスがSerializableなクラスを継承する場合も同様に、static なネストしたクラスとして定義しましょう。