隠しフィールド変数

下のようなクラスがあったとする。


public class Hogeo {
private Class cls = Hogeo.class;
private String class$0;
}

このクラス、実はコンパイルが通らない。
エラー内容は「Duplicate field Hogeo.class$0」。
class$0というフィールドが重複しているらしいけど、
ソースコード上では明らかに重複してない。
なんでだろ。


ちょっと調べるために、「private String class$0;」の部分を
コメントアウトして、下のようなコードでフィールド一覧を抽出してみた。


public static void main(String[] args) {
Field[] fields = Hogeo.class.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
System.out.println(i + ": " + fields[i]);
}
}
実行結果はこんな感じ。

0: private java.lang.Class test.Hogeo.cls
1: static java.lang.Class test.Hogeo.class$0
なぜかClass型の変数class$0が存在してる〜。
うーみゅ。


もうちょっと調べてみた。
フィールドやらメソッドやらはクラスファイル上ではAttribute
という情報を持っているらしい。
なので、こいつをJavassistで表示させてみた。
# Javassistの本来の使い方ではないけど、気にせず。


まず、てきとうコード。


public static void main(String[] args) {
try {
CtClass ctCls = ClassPool.getDefault().get("test.Hogeo");
CtField[] ctFields = ctCls.getDeclaredFields();

for (int i = 0; i < ctFields.length; i++) {
System.out.println(i + ": " + ctFields[i]);
List attrs = ctFields[i].getFieldInfo().getAttributes();

for (int j = 0; j < attrs.size(); j++) {
AttributeInfo attrInfo = (AttributeInfo) attrs.get(j);
System.out.println(" " + attrInfo.getName());
}
}
} catch (NotFoundException e) {
e.printStackTrace();
}
}

そして、実行結果。

0: test.Hogeo.cls:Ljava/lang/Class;
1: test.Hogeo.class$0:Ljava/lang/Class;
Synthetic
をを。
class$0の方にはSyntheticというAttributeがある。


Synthetic Attributeは、ここらへんに書いてあるやつっぽい。
http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#43817
さえない英語力で訳してみると、ソースコード上にないクラスメンバには
このAttributeが付けられるらしい。


なんかよくわからんけど、「foo.class」というコードは
クラスファイル上ではフィールド変数を使う形で表現されるんですかね。
メソッド内で「foo.class」と実装しても隠しフィールドが見えたので。




コンパイラの仕様みたいなのからわかるかなーと思ったけど
わたくしめの力では、ここらへんが限界っすorz
まぁ、class$0なんて変数名を使うことはないし、どうでもいい話題だなー。