thisはメソッドとフィールドで微妙に違う件

JavaScriptのthisではまった - No Programming, No LifeJava の this について - SiroKuro Pageの話。

Javaでは動的に付けたり外したりできず、class宣言時にすべてが決まってしまうから、thisが参照するインスタンスは固定的にできるわけですね。
JavaScriptのthisではまった - No Programming, No Life

実はこれはダウト。Java では継承があるから、事実上 this に入るクラスを静的に決定することは不可能。そして final クラスにしたとしても、どのインスタンスが入るかを決めるのは難しいわけです。
Java の this について - SiroKuro Page

元記事に対して「どのオブジェクト上で走るかの話で、javaの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".
    }
}

普通はフィールドはオーバライドしないと思うけど、言語仕様上は可能なわけで。微妙に気をつけようと思いました。

追記

実行時に,参照される実際のオブジェクトのクラスではなく,その Primary 式の型だけが,使用するフィールドを決定する際に使用されることに注意すること。
JavaŒ¾Œê‹K’è Ž®