何故staticの変数、メソッドにインスタンスを使ってアクセスしたらいけないのか


題名の通り、何故`static`の変数にインスタンスを使ってアクセスをしたらいけないのでしょうか。

* * *

Javaでは、`static`な変数に`非static`にアクセスしようとすると警告が出ます。

[code]

    class Instance{
      public static final name="Static Field";
    }
    class Access{
      public static void main(String[] args){
        System.out.println(new Instance().name);
        //警告(static-access)
      }
    }
    
[/code]

このコードだと、インスタンスを無駄に生成している。ということで納得いきます。

ここも参考にしました。

[Google Java Style](https://kazurof.github.io/GoogleJavaStyle-ja/#s6-3-static-
members):

> staticなメンバーを修飾する必要がある場合はクラス名を使う。そのクラスの変数や式経由で使ってはならない。

ですが次のような時はどうでしょう。

[code]

    public class Main{
      public static void main(String[] args){
        Class<?> cls = Class.forName(args[1]);
        Instance instance=(Instance)cls.newInstance();
        Load.load(instance);
      }
    }
    class Load{
      public static load(Instance instance){
        //警告発生
        System.out.print(instance.explanation);
      }
    }
    abstract class Instance{
      public static explanation;
    }
    
[/code]

このコードでは、Instanceを継承したクラスのstaticフィールドにアクセスするのが目的なのですが、`Instance`を継承したクラスの名前は決まっているわけではないので

[code]

    クラス名.フィールド名
    
[/code]

でアクセスすることが出来ない(出来るとしても少し手間がかかる)と思います。  
警告自体は

[code]

    @SuppressWarning("static-access")
    
[/code]

で抑えられるのですがどうしてなのかなと思って投稿しました。




> `Instance`を継承したクラスの名前は決まっているわけではないので
[code]

>     クラス名.フィールド名
>  
[/code]

>
> でアクセスすることが出来ない(出来るとしても少し手間がかかる)と思います。

静的メンバは継承することはできても、派生クラスで同じ名前の静的メンバを定義した場合には基底クラスのそれを隠し、別のメンバとして扱われます。

[code]

    public static load(Instance instance){
      System.out.print(instance.explanation);
    }
    
[/code]

またこれはインスタンス経由で静的メンバにアクセスしているように見えますが、どのクラスの静的メンバにアクセスするかはコンパイル時に決定します。ここでは
`Instance` 型の変数ですから、（派生クラスではなく）`Instance.explanation` へアクセスするようコンパイルされます。

例えば次のコードで `foo` と `bar` は同じインスタンスのはずですが、どの型の変数を通してアクセスするかで結果が変わっています。

[code]

    class Sample
    {
        public static void main (String[] args)
        {
            Derived foo = new Derived();
            Base bar = foo;
            System.out.println("Derived foo: " + foo.name); // => Derived
            System.out.println("Base bar: " + bar.name); // => Base
        }
    }
    
    abstract class Base {
      public static String name = "Base";
    }
    
    class Derived extends Base {
      public static String name = "Derived";
    }
    
[/code]

実行例 <http://ideone.com/TavNp3>

ですから、冒頭のような場面でも型名の代わりに変数名を使えば動的に解決してくれる、なんてことはありません。もし「実行時の型に応じて静的フィールド
`explanation` にアクセスする」といった必要があれば、直接アクセスする代わりにインスタンスメソッドを経由すべきかと思います。（例
<http://ideone.com/heb9K0>）

インスタンスメンバと紛らわしく、あえてこのような記法をする必要性も薄いですから、ミスの可能性が高いとして警告を発するのでしょう。

参考

  * [Are static methods inherited in Java? - Stack Overflow](https://stackoverflow.com/a/10292034/2818869)
  * The Java™ Tutorials  
[Overriding and Hiding
Methods](http://docs.oracle.com/javase/tutorial/java/IandI/override.html)  
[Hiding
Fields](http://docs.oracle.com/javase/tutorial/java/IandI/hidevariables.html)




> `Instance`を継承したクラスの名前は決まっているわけではないので
[code]

>     クラス名.フィールド名
>  
[/code]

>
> でアクセスすることが出来ない(出来るとしても少し手間がかかる)と思います。

静的メンバは継承することはできても、派生クラスで同じ名前の静的メンバを定義した場合には基底クラスのそれを隠し、別のメンバとして扱われます。

[code]

    public static load(Instance instance){
      System.out.print(instance.explanation);
    }
    
[/code]

またこれはインスタンス経由で静的メンバにアクセスしているように見えますが、どのクラスの静的メンバにアクセスするかはコンパイル時に決定します。ここでは
`Instance` 型の変数ですから、（派生クラスではなく）`Instance.explanation` へアクセスするようコンパイルされます。

例えば次のコードで `foo` と `bar` は同じインスタンスのはずですが、どの型の変数を通してアクセスするかで結果が変わっています。

[code]

    class Sample
    {
        public static void main (String[] args)
        {
            Derived foo = new Derived();
            Base bar = foo;
            System.out.println("Derived foo: " + foo.name); // => Derived
            System.out.println("Base bar: " + bar.name); // => Base
        }
    }
    
    abstract class Base {
      public static String name = "Base";
    }
    
    class Derived extends Base {
      public static String name = "Derived";
    }
    
[/code]

実行例 <http://ideone.com/TavNp3>

ですから、冒頭のような場面でも型名の代わりに変数名を使えば動的に解決してくれる、なんてことはありません。もし「実行時の型に応じて静的フィールド
`explanation` にアクセスする」といった必要があれば、直接アクセスする代わりにインスタンスメソッドを経由すべきかと思います。（例
<http://ideone.com/heb9K0>）

インスタンスメンバと紛らわしく、あえてこのような記法をする必要性も薄いですから、ミスの可能性が高いとして警告を発するのでしょう。

参考

  * [Are static methods inherited in Java? - Stack Overflow](https://stackoverflow.com/a/10292034/2818869)
  * The Java™ Tutorials  
[Overriding and Hiding
Methods](http://docs.oracle.com/javase/tutorial/java/IandI/override.html)  
[Hiding
Fields](http://docs.oracle.com/javase/tutorial/java/IandI/hidevariables.html)

