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


94

私は最近、Eclipseで警告を確認していて、これに遭遇しました:

静的警告

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

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

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

はい、オフにできることはわかっていますが、オンにする理由知りたいですか?

可能なすべてのメソッドをstaticとして宣言するのが良いのはなぜですか?

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

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

1日の終わりに、これを「無視」するだけにするか、それとも100以上の警告を修正する必要がありますか?

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


確かではありませんが、これは単にプログラミングの助けと見なすことができます。警告は、単に注意すべきことを示すものです。
James P.

1
これらのメソッドが実行する機能の種類に興味があります。これらがたくさんあると、何かがうまくいかなかったのかもしれません。
ArjunShankar

回答:


132

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

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

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

そのため、static実際に静的コントラクトを実装しているときにメソッドを宣言することをお勧めします。

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

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

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

1
+1自分の足を撃ち落とす機会を最小限に抑え、方法を理解するために必要な知識を減らすことについてです。
Peter Lawrey、2012年

7
さらに、独立した自己完結型の関数を呼び出すためだけにインスタンスを取得する必要はありません。
Marko Topolnik

1
したがって、他の世界では、インスタンス変数を使用していないメソッドはすべて静的として宣言する必要がありますか?私は、メソッドが望ましい場合にのみ静的であると常に思っていました(ユーティリティメソッドのように)。
Petr Mensik

18
@PetrMensik privateメソッドを静的に宣言できる場合、ほとんどの場合は静的でなければなりません。他のアクセスレベルについては、動的ディスパッチなど、考慮すべき他の要素があります。
Marko Topolnik

1
@JamesPoulson動的なメソッドのディスパッチ、つまりobject.method()呼び出すメソッドを選択するために実行しているときに発生すること。
Marko Topolnik

15

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

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

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

インスタンス変数が必要なく、メソッドがプライベート(外部から呼び出せない)またはファイナル(オーバーライドできない)である場合、静的メソッドではなく通常のメソッドにする理由はありません。静的メソッドは、それを使用して実行できる処理が少ないため、本質的に安全です(インスタンスを必要とせず、暗黙的なthisオブジェクトもありません)。


7

パフォーマンスに関する情報はありませんが、コードがタイプに基づいて動的ディスパッチを行う必要がないため、パフォーマンスはほとんど向上しないと思います。

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

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


7
静的メソッドをテストするのは簡単です(自己完結型である限り、特に静的メソッドの主要なターゲットである純粋な関数)。その場合、オブジェクト指向の概念は何も提供しません。また、ファーストクラス関数の概念は、静的メソッドの概念とはほとんど関係がありません。既存の静的メソッドの機能を実行するファーストクラスの関数が必要な場合は、それを実装するための少数の文字の問題です。
Marko Topolnik

5
テスト容易性の発言は、おそらく静的メソッドを直接対象としていなかったでしょうが、静的メソッドはモックするのがはるかに難しいため、静的メソッドを呼び出すメソッドのテストは複雑になります。
Buhb

2
JMockitPowerMockGroovyなどの強力なモックフレームワークを使用すると、静的メソッドを簡単にモックできます。
ジェフオルソン

これらはprivate staticメソッドなので、モックする必要はありません
Blundell

3

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

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

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

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

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

3.元の投稿者はまた、次のように質問します。この種の設計変更は警告によって示されることがあります。

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


1

メソッドのスコープがどのように変化するかについてのサミュエルの回答を参照してください。おそらく、これがメソッドを静的にする主な側面です。

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

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

ただし、このパフォーマンスへの影響はごくわずかです。したがって、それはスコープに関するすべてです。


1

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

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

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


「メソッドを呼び出してもオブジェクトの状態を変更できない」-信じられないほど誤解を招く可能性があります。私はあなたについては知りませんが、クラスの静的属性はオブジェクトの状態の一部であると私は確かに考えています。
Adam Parkin 14

0

さて、Eclipseのドキュメントは問題の警告について述べています:

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

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

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

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


0

速度の違いのためにいくつかの数字が欠けていました。だから私はそれほど簡単ではないことが判明したそれらをベンチマークしようとしました: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.G1ConcMarkStepStep

そして私自身の結果は:

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;
    }

}

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

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;
    }

}

さまざまなAndroidデバイスとバージョンで結果を確認するのは興味深いでしょう
Blundell

うん。興味があると思いました:)しかし、Androidソフトウェアを構築していて、おそらくAndroidデバイスが開発ステーションに接続されているため、コードを選択して実行し、結果を共有することをお勧めします。
Scheintod 14

-2

staticとして宣言できるメソッドは、インスタンス化を必要としないメソッドです。

public class MyClass
{
    public static string InvertText(string text)
    {
        return text.Invert();
    }
}

その後、そのクラスをインスタンス化せずに、他のクラスで呼び出すことができます。

public class MyClassTwo
{
    public void DoSomething()
    {
        var text = "hello world";
        Console.Write(MyClass.InvertText(text));
    }
}

...しかし、それはおそらくあなたがすでに知っていることです。メソッドがインスタンス変数を使用しないことをより明確にすることを除いて、それ自体は実際の利点を提供しません。

つまり、完全にオフにするだけで安全です。他のクラスでメソッドを使用しないことがわかっている場合(この場合、メソッドはプライベートである必要があります)、静的である必要はまったくありません。


1
その言語は何ですか?その例はコンパイルできますか?ここでは静的なものはありません。
Piotr Perak 2012

確かに、私はInvertTextメソッドからの「静的」を忘れたようです。そして、それはc#に基づく例です
NeroS
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.