ややこしいのはthisではない

thisはメソッドとフィールドで微妙に違う件 - @katzchang.contextsを見て。Javaのthisがややこしいという話なんですが、ややこしいのはthisではないです。

結論としては、メソッドとフィールドの扱いは違うっぽい。

  • メソッドを参照する場合、thisは実行時のインスタンスを指す。
  • フィールドを参照する場合、thisは定義時のフィールドを指す。

メソッド(正確にはインスタンスメソッド)とフィールドでちがう、というのはあってるんですけど、thisは関係ありません。メソッドとフィールドで扱いがちがうのはthisに限らないんです。ただの変数でも、評価結果でも、インスタンスメソッドだけが特別扱いです。
実際、

検証

  • Hoge#toString()の中で使われるthisは、SubHogeクラスのインスタンス上で実行しても、Hogeクラスインスタンスのnameフィールドを示す。SubHogeクラスインスタンスのnameフィールドではない。
  • subHogeをSubHogeクラスとして扱う場合とHogeでキャストした場合とで、subHoge.nameの示す値が自然に変わってたりする。
public class Hoge {
    public String name = "Hoge";
    public void foo() {
        System.out.println(this.toString());
    }
    @Override
    public String toString() {
        return this.name;
    }
    
    public static class SubHoge extends Hoge {
        public String name = "SubHoge";
    }
    
    public static void main(String...args) {
        //当然のケース。
        Hoge hoge = new Hoge();
        hoge.foo(); //prints "Hoge".
        System.out.println(hoge.name);
        
        //サブクラスのオブジェクトの動作。
        SubHoge subHoge = new SubHoge();
        subHoge.foo(); //prints "Hoge".
        System.out.println(subHoge.name); //prints "SubHoge".
        System.out.println(((Hoge)subHoge).name); //prints "Hoge".
    }
}

と、検証されてるんですが、

System.out.println(subHoge.name); //prints "SubHoge".
System.out.println(((Hoge)subHoge).name); //prints "Hoge".

と、thisは全く関係なくフィールドへのアクセスが静的な型で決定される事が確認できます。

subHoge.foo(); //prints "Hoge".

となるのは、fooメソッドHogeクラスで定義されているためにここでのthisはHoge型の変数(というか暗黙の引数)となるからで、これはフィールドアクセスだろうがメソッドアクセスだろうが一緒です。