web-dev-qa-db-ja.com

メソッドを静的として宣言することの利点は何ですか

私は最近、Eclipseでの警告を調べてきましたが、これに遭遇しました。

static warning

メソッドを静的として宣言できる場合は、コンパイラに警告が表示されます。

[編集] Eclipseヘルプ内での正確な引用。プライベートおよびファイナルに重点を置いています。

有効にすると、コンパイラはprivateまたはfinalであり、静的メンバーのみを参照するメソッドに対してエラーまたは警告を発行します。

はい、私はそれをオフにできることを知っていますが、オンにする理由を知っていますか?

なぜすべてのメソッドを静的として宣言するのが良いのでしょうか?

これにより、パフォーマンス上の利点が得られますか? (モバイルドメイン内)

メソッドを静的として指摘し、インスタンス変数を使用しないことを示しているので、utilsスタイルクラスに移動できると思いますか?

一日の終わりに、これを「無視」をオフにするか、それが与えた100以上の警告を修正する必要がありますか?

コンパイラーはとにかくこれらのメソッドをインライン化するだけなので、これはコードをdirtyする単なる追加キーワードだと思いますか? (最終的な変数を宣言するわけではありませんが、可能です)。

93
Blundell

メソッドを書くときはいつでも、与えられたスコープで契約を履行します。スコープが狭いほど、バグを書く可能性は小さくなります。

メソッドが静的な場合、非静的メンバーにアクセスできません。したがって、スコープは狭くなります。したがって、を必要とせず、サブクラスでも契約を履行するために非静的メンバーを必要としない場合、なぜこれらのフィールドへのアクセスをメソッドに与えるのですか?この場合メソッドstaticを宣言すると、コンパイラは、使用するつもりのないメンバーを使用していないことを確認できます。

さらに、コードを読んでいる人が契約の性質を理解するのに役立ちます。

そのため、実際に静的コントラクトを実装しているときにメソッドstaticを宣言するのが良いと考えられます。

場合によっては、メソッドはクラスのインスタンスに関連するものだけを意味し、その実装では実際には非静的フィールドまたはインスタンスを使用しないことがあります。そのような場合、メソッドstaticをマークしません。

staticキーワードを使用しない場所の例:

  • 何もしない拡張フック(ただし、サブクラスのインスタンスデータで何かを行うことができます)
  • サブクラスでカスタマイズ可能な非常に単純なデフォルトの動作。
  • イベントハンドラーの実装:実装はイベントハンドラーのクラスによって異なりますが、イベントハンドラーインスタンスのプロパティは使用しません。
130
Samuel Rossille

ここには最適化の概念はありません。

staticメソッドはstaticです。これは、そのメソッドが必要であるという理由だけで、そのクラスを囲むクラスのインスタンスに依存しないことを明示的に宣言するためです。そのため、ドキュメントに記載されているEclipse警告:

有効にすると、コンパイラーはプライベートまたはファイナルで、静的メンバーのみを参照するメソッドに対してエラーまたは警告を発行します。

インスタンス変数が不要で、メソッドがプライベート(外部から呼び出せない)またはfinal(オーバーライドできない)の場合、静的メソッドではなく通常のメソッドにする理由はありません。静的メソッドは、より少ない処理を許可されているからといって、本質的に安全です(インスタンスを必要とせず、暗黙のthisオブジェクトはありません)。

13
Jack

コードは型に基づいて動的なディスパッチを行う必要がないため、パフォーマンスに関する情報はありません。

ただし、静的メソッドへのリファクタリングに対するはるかに強力な議論は、現在静的を使用することは悪い習慣と見なされるということです。静的メソッド/変数はオブジェクト指向言語にうまく統合されず、適切にテストするのも困難です。これが、いくつかの新しい言語が静的メソッド/変数の概念を完全に見捨てる、またはOO(Scalaのオブジェクトなど)でより適切に動作する方法で言語に内部化しようとする理由です。

ほとんどの場合、入力としてパラメーターのみを使用し、それを使用して出力を生成する関数(ユーティリティ/ヘルパー関数など)を実装するには、静的メソッドが必要です。現代の言語では、必要ありません。 Java 8はラムダ式が統合されるため、すでにこの方向に進んでいます。

6
Istvan Devai

1。メソッドstaticを宣言すると、パフォーマンスがわずかに向上しますが、より便利なのは、オブジェクトインスタンスを用意せずに使用できることです。 (たとえば、ファクトリメソッドやシングルトンの取得について考えてください)。また、メソッドの性質を伝えるという文書化の目的にも役立ちます。このドキュメンテーションの目的は無視してはなりません。メソッドの性質についてコードの読者とAPIのユーザーに即座にヒントを与え、元のプログラマーの思考のツールとしても機能するためです。また、まっすぐ考えてより良い品質のコードを生成します(個人的な経験に基づいて考えますが、人々は異なります)。たとえば、型で動作するメソッドと型のインスタンスで動作するメソッドを区別することは論理的であり、したがって望ましいです( C#質問へのコメントでのジョンスキート によって指摘されているように)。

staticメソッドのさらに別の使用例は、手続き型プログラミングインターフェイスを模倣することです。 Java.lang.System.println() クラスとその中のメソッドと属性を考えてください。クラス Java.lang.Systemは、インスタンス化可能なオブジェクトではなく、グループ化ネームスペースのように使用されます。

2。Eclipse(または他のプログラムされたまたは他の種類の-生体合成可能または非生体合成可能-エンティティ)は、どのメソッドが静的?基本クラスがインスタンス変数にアクセスしていないか、非静的メソッドを呼び出していない場合でも、継承のメカニズムにより状況は変化します。サブクラスを継承してメソッドをオーバーライドできない場合にのみ、メソッドが実際にstaticとして宣言できることを100%確実に主張できます。メソッドのオーバーライドは、次の2つの場合に正確に不可能です。

  1. private(サブクラスで直接使用することはできず、原則としてそれを認識していません)、または
  2. final(サブクラスからアクセスできる場合でも、メソッドを変更してインスタンスデータまたは関数を参照する方法はありません)。

したがって、Eclipseオプションのロジック。

3。元のポスターは次のようにも尋ねます: "メソッドを静的として指摘し、インスタンス変数を使用して、utilsスタイルクラスに移動できますか? "これは非常に良い点です。このような設計変更は、警告によって示される場合があります。

これは非常に便利なオプションです。Eclipseを使用し、Javaでプログラミングする場合、個人的に有効にすることを確認します。

3
FooF

メソッドのスコープがどのように変化するかについてのSamuelの回答を参照してください。これがメソッドを静的にする主な側面だと思います。

パフォーマンスについても質問しました:

静的メソッドの呼び出しには、パラメーターとしての暗黙的な「this」参照が必要ないため、パフォーマンスがわずかに向上する可能性があります。

ただし、このパフォーマンスへの影響は非常にわずかです。したがって、スコープがすべてです。

1
Black

Androidパフォーマンスガイドラインから:

仮想よりも静的を優先オブジェクトのフィールドにアクセスする必要がない場合は、メソッドを静的にします。呼び出しは約15%〜20%高速になります。メソッドを呼び出すことでオブジェクトの状態を変更できないことをメソッドのシグネチャから知ることができるため、これも良い習慣です。

http://developer.Android.com/training/articles/perf-tips.html#PreferStatic

1
Blundell

さて、Eclipseのドキュメントには、問題の警告について書かれています。

メソッドは静的にすることができます

有効にすると、コンパイラーはプライベートまたはファイナルで静的メンバーのみを参照するメソッドに対してエラーまたは警告を発行します

私はそれがほとんどすべてを言っていると思います。メソッドがprivateおよびfinalであり、静的メンバーのみを参照している場合、問題のメソッドも静的と宣言される可能性があり、これにより、静的コンテンツのみにアクセスすることを意図していることが明らかになります。

正直なところ、その背後に他の神秘的な理由はないと思います。

0
Edwin Dalorzo

私は速度の違いのためにいくつかの数字が欠落していました。だから私はそれらをベンチマークしようとしましたが、それほど簡単ではありませんでした: Javaループはいくつかの実行/ JITの障害の後に遅くなりますか?

最終的にCaliperを使用しましたが、結果はテストを手動で実行した場合と同じです。

静的/動的呼び出しに測定可能な差はありません。少なくともLinux/AMD64/Java7には違いはありません。

キャリパーの結果はこちら: https://microbenchmarks.appspot.com/runs/1426eac9-36ca-48f0-980f-0106af064e8f#r:scenario.benchmarkSpec.methodName,scenario.vmSpec.options.CMSLargeCoalSurplusPercent 、scenario.vmSpec.options.CMSLargeSplitSurplusPercent、scenario.vmSpec.options.CMSSmallCoalSurplusPercent、scenario.vmSpec.options.CMSSmallSplitSurplusPercent、scenario.vmSpec.options.FLSLargestBlockCoalesceProximity、scenario.vmSpec.options.G1ConcMarkStepDurationMillis

私自身の結果は次のとおりです。

Static: 352 ms
Dynamic: 353 ms
Static: 348 ms
Dynamic: 349 ms
Static: 349 ms
Dynamic: 348 ms
Static: 349 ms
Dynamic: 344 ms

キャリパーテストクラスは次のとおりです。

public class TestPerfomanceOfStaticMethodsCaliper extends Benchmark {

    public static void main( String [] args ){

        CaliperMain.main( TestPerfomanceOfStaticMethodsCaliper.class, args );
    }

    public int timeAddDynamic( long reps ){
        int r=0;
        for( int i = 0; i < reps; i++ ) {
            r |= addDynamic( 1, i );
        }
        return r;
    }

    public int timeAddStatic( long reps ){
        int r=0;
        for( int i = 0; i < reps; i++ ) {
            r |= addStatic( 1, i );
        }
        return r;
    }

    public int addDynamic( int a, int b ){

        return a+b;
    }

    private static int addStatic( int a, int b ){

        return a+b;
    }

}

そして、私自身のテストクラスは:

public class TestPerformanceOfStaticVsDynamicCalls {

    private static final int RUNS = 1_000_000_000;

    public static void main( String [] args ) throws Exception{

        new TestPerformanceOfStaticVsDynamicCalls().run();
    }

    private void run(){

        int r=0;
        long start, end;

        for( int loop = 0; loop<10; loop++ ){

            // Benchmark

            start = System.currentTimeMillis();
            for( int i = 0; i < RUNS; i++ ) {
                r += addStatic( 1, i );
            }
            end = System.currentTimeMillis();
            System.out.println( "Static: " + ( end - start ) + " ms" );

            start = System.currentTimeMillis();
            for( int i = 0; i < RUNS; i++ ) {
                r += addDynamic( 1, i );
            }
            end = System.currentTimeMillis();
            System.out.println( "Dynamic: " + ( end - start ) + " ms" );

            // Do something with r to keep compiler happy
            System.out.println( r );

        }

    }

    private int addDynamic( int a, int b ){

        return a+b;
    }

    private static int addStatic( int a, int b ){

        return a+b;
    }

}
0
Scheintod