Java内部クラスの判定

必要に迫られて任意のクラスが内部クラスかどうかを判定しなくてはならなくなった。普通の人はこんなものは不要と思う。妙な書き方をしてしまった妙なプログラム~クラスがずらずらと並べられたテーブルを前にして、これを判定する必要に迫られた。

ネストしたクラス、内部クラス、匿名クラス

内部クラスという言葉

ウェブ検索してみると、何人かの方が指摘されているのだが、ネストしたクラスは必ずしも内部クラス(Inner class)では無いようだ。つまり、以下のBarは内部クラスではない。

class Foo {
  static class Bar {
  }
}

私も適当に内部クラスと呼んでいたのだが、間違いだ。

例えば、Oracleの文書を見てみる。https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

A nested class is a member of its enclosing class. Non-static nested classes (inner classes) have access to other members of the enclosing class, even if they are declared private. Static nested classes do not have access to other members of the enclosing class.

ネストしたクラスとは、それを取り囲むクラスのメンバーである。staticでないネストしたクラス(内部クラス)は、それを取り囲むクラスのメンバーへのアクセスが可能になる。たとえそれらがprivateと宣言されていてもである。staticなネストしたクラスは、それを取り囲むクラスの他のメンバーへのアクセスはできない。

つまり、入れ子になったクラスはネストしたクラス(Nested Class)と呼ばれるべきであり、その中のstaticでは無いものだけが内部クラス(Inner Class)である。

では匿名クラスは?

匿名クラスも内部クラスの一種だとは思うのだが、しかしOracleの文書では特に匿名クラスが内部クラスだとは書いてはいない。

https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html

多くのウェブページで、匿名内部クラス(Anonymous Inner class)という言葉を使ってはいるが、ざっと検索してみたところOracleの正式文書は見つからなかった。

内部クラスを検出するには?

ともあれ、以下では匿名クラスも内部クラスのうちと仮定する。

そして、内部クラスの検出のためには、isMemberClass()、getModifier()、getEnclosingClass()を使えばよいようだ。以下のようなプログラムを書いてみる。


import java.lang.reflect.*; public class Sample { static class Foo { } class Bar { } static void checkClass(Class<?>clazz) { System.out.println("\n" + clazz); System.out.println("memberClass:" + clazz.isMemberClass()); System.out.println("enclosingClass:" + clazz.getEnclosingClass()); System.out.println("static:" + ((clazz.getModifiers() & Modifier.STATIC) != 0)); } static void checkRunnable(Runnable runnable) { checkClass(runnable.getClass()); } public static void main(String[]args) { checkClass(Sample.class); checkClass(Foo.class); checkClass(Bar.class); checkClass(new Foo() {}.getClass()); checkClass(new Sample().new Bar() {}.getClass()); checkRunnable(new Runnable() { public void run() {} }); checkRunnable(()-> {}); } }

結果は以下だ。


class com.cm55.samples.inner.Sample memberClass:false enclosingClass:null static:false class com.cm55.samples.inner.Sample$Foo memberClass:true enclosingClass:class com.cm55.samples.inner.Sample static:true class com.cm55.samples.inner.Sample$Bar memberClass:true enclosingClass:class com.cm55.samples.inner.Sample static:false class com.cm55.samples.inner.Sample$1 memberClass:false enclosingClass:class com.cm55.samples.inner.Sample static:false class com.cm55.samples.inner.Sample$2 memberClass:false enclosingClass:class com.cm55.samples.inner.Sample static:false class com.cm55.samples.inner.Sample$3 memberClass:false enclosingClass:class com.cm55.samples.inner.Sample static:false class com.cm55.samples.inner.Sample$$Lambda$1/531885035 memberClass:false enclosingClass:null static:false

この結果からわかることとしては、内部クラスはenclosingClassがnullでなく、かつstaticがfalseのものということになる。

しかし、ラムダのクラス(?)は例外だ。この判定方法では「内部クラスでは無い」ことになってしまう。が、とりあえず必要が無いので後回しにする。