キャストなしでスーパークラス型のthis参照を表す式

概要

super参照っぽいものを作ります。

前提

thisは参照を表す式として使えますが、superは参照を表す式として使えません。

            // thisは参照
            System.out.println(this);
            // コンパイルエラー。superは参照ではない
//            System.out.println(super);

隠蔽と式の型

式の型をスーパークラスにすることで、隠蔽されたスーパークラスのフィールドにアクセスすることができます。

たとえば、クラスCと、そのサブクラスSubCがあるとします。
CとSubCは同名のフィールドfieldを持っているとします。

するとSubC内ではthis.fieldはサブクラスのフィールドを表すことになります。 (1)
ところが、Cでthisをキャストすると、式の型がCになるので、スーパークラスのフィールドを表すことになり、アクセスが可能になります。 (2)

参考: Javaクラス使用メモ(Hishidama's Java Class use Memo)

キャストを除く

以上をふまえて、super参照っぽいもの、つまり式の型がスーパークラスで参照がthisを表すようなそんな式を作ってみます。
以下がそのコードです。

package test;

public class SuperTest {

    private static class C {
        String field = "C.field";
        
        public void printThis() {
            // thisは参照
            System.out.println(this);
            // コンパイルエラー。superは参照ではない
//            System.out.println(super);
        }
    }
    
    private static class SubC extends C {
        // hiding
        String field = "SubC.field";
        
        public void printField() {
            // (1) SubC.field
            System.out.println(this.field);
            // (2) C.field
            System.out.println(((C) this).field);
            // (3) (? extends C).field
            System.out.println(super.getClass().cast(this).field);
            // (4) C.field
            System.out.println((true ? this : super.getClass().cast(this)).field);
            // (5) C.field
            System.out.println((true ? this : super.getClass().cast(null)).field);
            // (6) C.field
            System.out.println((true ? this : super.getClass().getEnumConstants()[-1]).field);
            
            System.out.println(super.field);
        }
    }
    
    public static void main(final String[] args) {
        
        new SubC().printField();
    }

}

簡単なのは(2)と同様thisをスーパークラスの型 (ここではC) にキャストすることですが、明示的なキャストが必要ではスーパークラスを知る必要があるので、テンプレートなどに使いづらいですね。

そこでまずキャストを動的にします。具体的には、SubC中でsuper.getClass() の型が Class<? extends C>*1になる*2ことを利用すると、super.getClass().cast(this)は、 (? extends C)*3型になります。
これだけでもスーパークラスのフィールドを参照することができるようになります。 (3)

ただ、(? extends C)型はC型でないのでこれではまだ完全ではありません。
そこで、条件式の型が2つの式の共通のスーパータイプになることを利用して、thisとsuper.getClass.cast(this)の共通のスーパータイプを取ればC型を得られます。 (4)
条件式の2つの式うち、片方は型を変換するためだけの式なので、片方はthis参照を表す式でなくてもかまいません。 (5)
さらに、片方は評価されないので、評価すると例外がスローされるような式でも型が合っていれば問題ありません。
そこで、Classオブジェクトの型変数を戻り値型に含むgetEnumConstantsも使えます。*4 (6)

かくして、キャストなしでスーパークラスの型をもつthisを作ることができました。

参考まで実行結果

SubC.field
C.field
C.field
C.field
C.field
C.field
C.field

結論

スーパークラスの型をもつthis参照を表す式は

true ? this : super.getClass().getEnumConstants()[-1]

で作る

*1:捕捉変換されます

*2:C.getClass()の型はClass<? extends |C|> http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#getClass%28%29

*3:捕捉変換されます

*4:newInstanceは検査例外をスローするのでおすすめしません