web-dev-qa-db-ja.com

Javaでは、クラスの静的メソッドに不利な点はありますか?

ルール(または経験則)が私のコーディング環境に課せられており、クラス変数を使用しない、変更しない、または処理にインスタンス変数を必要としないメソッドはすべて静的になっていると仮定しましょう。これを行うことには、固有のコンパイル時間、ランタイム、またはその他の欠点がありますか?

(さらに明確にするために編集)

質問はいくぶん自由回答で曖昧だったので、申し訳ありません。私の質問の意図は、主に「ヘルパー」メソッドのコンテキストにありました。すでに行っている静的メソッドのホルダーとしてのユーティリティクラス(インスタンス化できないようにプライベートCTORを使用)。ここでの私の質問は、メインクラスAPIを支援するこれらの小さなメソッドに沿ったものでした。

実際の作業を行うクラスに4つまたは5つのメインAPI /インスタンスメソッドがあるかもしれませんが、そうする過程で、APIメソッドへの入力パラメーターのみで機能し、内部状態ではない可能性があるいくつかの共通機能を共有します。これらは、私が通常、独自のヘルパーメソッドに引き出すコードセクションです。クラスの状態にアクセスする必要がない場合は、静的にします。

したがって、私の質問は、これは本質的に悪い考えですか、そうであれば、なぜですか? (またはなぜそうではないのですか?)

36

主な欠点は、実行時にメソッド実装を交換、オーバーライド、または選択できないことです。

29
cherouvim

私の意見では、Javaで静的メソッドを使用しない理由は4つあります。これは、静的メソッドがneverに適用可能であると言っているのではなく、一般的には回避する必要があると言っているだけです。

  1. 他の人が指摘したように、静的メソッドは単体テストでモックアウトすることはできません。クラスがDatabaseUtils.createConnection()に依存している場合、その依存クラスとそれに依存するクラスは、実際にデータベースまたはある種の「テスト」フラグがないとテストすることがほとんど不可能ですDatabaseUtils。後者の場合、DatabaseConnectionProviderインターフェースの実装が2つあるように思えます-次のポイントを参照してください。

  2. 静的メソッドがある場合、その動作はあらゆる場所のすべてのクラスに適用されます。条件付きで動作を変更する唯一の方法は、フラグをパラメーターとしてメソッドに渡すか、静的フラグをどこかに設定することです。最初のアプローチの問題は、すべての呼び出し元のシグネチャが変更され、フラグが追加されるにつれてすぐに煩雑になることです。 2番目のアプローチの問題は、次のようなコードが至る所に配置されることです。

     boolean oldFlag = MyUtils.getFlag(); 
     MyUtils.someMethod(); 
     MyUtils.setFlag(oldFlag); 
    

    この問題が発生した共通ライブラリの1つの例は、Apache Commons Langです。StringUtilsBeanなどを参照してください。

  3. オブジェクトはClassLoaderごとに1回読み込まれます。つまり、実際には静的メソッドと静的変数の複数のコピーが無意識のうちに存在し、問題が発生する可能性があります。オブジェクトは一時的なものであるため、これは通常、インスタンスメソッドではそれほど問題になりません。

  4. 静的変数を参照する静的メソッドがある場合、それらはクラスローダーの存続期間中ずっと使用され、ガベージコレクションは行われません。これらが情報(キャッシュなど)を蓄積し、注意しないと、アプリケーションで「メモリリーク」が発生する可能性があります。代わりにインスタンスメソッドを使用すると、オブジェクトの寿命が短くなる傾向があるため、しばらくするとガベージコレクションが行われます。もちろん、インスタンスメソッドでもメモリリークが発生する可能性があります。しかし、それはそれほど問題ではありません。

お役に立てば幸いです。

25
Eric Galluzzo

パフォーマンスの利点はごくわずかです。状態に依存しないものには静的メソッドを使用します。静的メソッド呼び出しですぐにわかるように、これによりコードが明確になり、インスタンスの状態が関係していないことがわかります。

17
Steve B.

これは私の職業生活の中で過去4年間議論してきた点であり、私はこの質問が本当に好きです。静的メソッドは、状態を持たないクラスに対しては非常に理にかなっています。しかし、最近、私は幾分修正されました。

静的メソッドを持つユーティリティクラスは良い考えです。

多くの場合、ビジネスロジックを運ぶサービスクラスはステートレスになる可能性があります。最初は常に静的メソッドを追加していましたが、Springフレームワーク(およびいくつかの一般的な読み物)に慣れると、uがこのクラスにモックサービスを簡単に挿入できないため、これらのメソッドは独立したユニットとしてテストできなくなることに気付きました。例えば。静的メソッドが別のクラスの別の静的メソッドを呼び出す場合、JUnitテストが実行時にダミーの実装を注入してパスを短絡させることはできません。

そのため、他のクラスやメソッドをほとんど呼び出す必要がないユーティリティの静的メソッドがあると静的になる可能性があるという考えに少し落ち着きました。ただし、サービスクラスは一般に静的ではありません。これにより、オーバーライドなどのOOP機能を活用できます。

また、シングルトンインスタンスクラスがあることで、静的クラスに非常によく似たクラスでもOOPの概念を使用できるようになります。

7
Fazal

Disadvantage -> Static

メンバーはクラスの一部であるため、アプリケーションが終了するまでメモリに残ります。ガベージコレクションはできません。静的メンバーを過剰に使用すると、製品の設計に失敗し、静的/手続き型プログラミングに対処しようとしていると予測されることがあります。オブジェクト指向の設計が損なわれていることを示しています。これにより、メモリオーバーフローが発生する可能性があります。

6
Reegan Miranda

それはすべて文脈の問題です。一部の人々は、考えられる状態のないユーティリティ関数を作成するときなど、静的が絶対的に望ましい例をすでに示しています。たとえば、配列で使用するさまざまなソートアルゴリズムのコレクションを作成している場合、メソッドをstatic以外にすると、状況が混乱します。コードを読み取るプログラマーは、なぜそれを静的にしないのか、そしてオブジェクトに対してステートフルなことをしているのかどうかを確認する必要があるでしょう。

public class Sorting {
  public static void quiksort(int [] array) {}
  public static void heapsort(int[] array) { }
}

そうは言っても、ある種のコードを記述し、特別な1回限りのコードがあると主張する人はたくさんいますが、後でそうであるとはわかりません。たとえば、変数の統計を計算するとします。だからあなたは書く:

public class Stats {
  public static void printStats(float[] data) { }
}

ここで悪いデザインの最初の要素は、プログラマが結果を一般的に使用するのではなく、単に出力することを意図していることです。 I/Oを計算に埋め込むのは、再利用するのが大変です。ただし、次の問題は、この汎用ルーチンが最大値、最小値、平均値、分散などを計算し、どこかに保存することです。どこ?オブジェクトの状態。それが本当に1回限りの場合は、静的にすることができますが、もちろん、2つの異なるものの平均を計算する必要があることに気づくでしょう。オブジェクトを複数回インスタンス化することができれば、それは非常に素晴らしいことです。 。

public class Stats {
  private double min,max,mean,var;
  public void compute(float data[]) { ... }
  public double getMin() { return min; }
  public double
}

Staticに対するひざの反応は、多くの場合、この種のことを静的に行う愚かさに対するプログラマの反応です。なぜなら、実際にどのケースが大丈夫で、どれが愚かであるかを実際に説明するよりも、決してそうしないと言う方が簡単だからです。

この場合、Javaはその点で非常に不愉快なので、実際にはオブジェクトを一種の参照専用パスとして使用しています。C++では、この種のことは参照として渡されたすべての状態を持つ関数でしたが、C++でも同じルールが適用されます。参照渡しの欠如のため、Javaはオブジェクトをより多く使用することを強制するだけです。

パフォーマンスに関する限り、通常のメソッドからの切り替えによる最大のパフォーマンス向上は、Javaのデフォルトであり、C++では仮想で手動で指定される動的なポリモーフィックチェックを実際に回避することです。

私が最後に試したとき、通常のメソッドよりもfinalメソッドを呼び出すことには3:1の利点がありましたが、finalより静的関数を呼び出すことには識別できませんでした。

あるメソッドを別のメソッドから呼び出す場合、JITはコードをインライン化できるほどスマートであることが多いことに注意してください。その場合、呼び出しはまったく行われません。そのため、どれだけ節約するかについてステートメントを作成することは非常に危険です。コンパイラーが関数を呼び出さなければならないときに、staticやfinalのように、必要な計算量が少ない関数を呼び出すことができれば問題はありません。

4
Dov

直面する主な問題は、必要に応じて新しい実装を提供できないことです。

それでも疑問がある場合(将来的に実装が変更されるかどうかにかかわらず)、実際の実装で常にプライベートインスタンスを使用できます。

 class StringUtil {
     private static StringUtil impl = new DefaultStringUtil();

     public static String nullOrValue( String s ) {
          return impl.doNullOrValue();
     }
     ... rest omitted 
  }

"some"の理由で、提供する実装クラスを変更する必要がある場合:

  class StringUtil {
     private static StringUtil impl = new ExoticStringUtil();

     public static String nullOrValue( String s ) {
          return impl.doNullOrValue(s);
     }
     ... rest omitted 
  }

ただし、状況によっては過剰な場合があります。

3
OscarRyz

静的メソッドを呼び出すために、クラスオブジェクトを作成する必要はありません。メソッドはすぐに使用できます。

クラスがすでにロードされていると仮定します。それ以外の場合は少し待機があります。 :-)

Staticは、機能コードを手続き型/状態設定コードから分離する良い方法だと思います。通常、機能コードは拡張機能を必要とせず、バグがある場合にのみ変更されます。

シングルトンなどのアクセス制御メカニズムとしてstaticを使用することもあります。

2
jagamot

いいえ、実際にそのアドバイスの理由は、それがパフォーマンス上の利点を提供することです。静的メソッドは少ないオーバーヘッドで呼び出すことができるため、thisへの参照を必要としないメソッドは静的にする必要があります。

1
Andrew Hare

いいえ、デメリットはありません。メソッドのインスタンスメンバーにアクセスしていない場合は、インスタンスメソッドとして使用する意味はありません。静的メソッドとして持つことは、プログラミングのスキルとして優れています。

さらに、これらのメソッドにアクセスするためにインスタンスを作成する必要がないため、メモリとガベージコレクションの時間を節約できます。

1
GuruKulki

不利な点があってはなりません。動的なルックアップを回避できるため、パフォーマンスはわずかに向上します(測定はできません)。

メソッドのように見えるようにするのではなく、関数として関数にタグを付けるのは便利です(そして、静的な "メソッド"は、メソッドではなく、実際には定義による、関数です)。

一般に、静的メソッドは不適切ですOOコードの臭い-これはおそらく、OOモデルが完全に統合されていないことを意味します。これは、使用するコードを認識できないライブラリですが、統合された非ライブラリコードでは、静的メソッドを調べて、どのパラメーターが最も密接に関連付けられているかを評価する必要があります。メンバーである可能性が高いです。そのクラスの。

静的メソッドがネイティブ値のみを取る場合、おそらく少数のクラスが欠落しています。また、ネイティブ変数またはライブラリオブジェクト(コレクションなど)を最小限に渡し、ビジネスロジックを持つクラスに含めるのではなく、それらを渡す必要もあります。

私が言っていることは、これが本当に問題である場合、モデリングの実践を再検討する必要があるかもしれないと思います-静力学は非常にまれであるため、これは問題でもありません。

1
Bill K

1つの欠点は、静的メソッドが一般的であり、使用に関する限り、異なるクラスに分散されている場合です。一般的なすべての静的メソッドをユーティリティクラスに配置することを検討してください。

1
setzamora

他の人が言ったように、それはわずかなパフォーマンス上の利点を提供し、優れたプログラミング実践です。唯一の例外は、メソッドをオーバーライドする目的でインスタンスメソッドにする必要がある場合ですが、それらは通常簡単に認識されます。たとえば、クラスがインスタンスメソッドのデフォルトの動作を提供する場合、たまたまインスタンス変数は必要ありませんが、それを明らかに静的にすることはできません。

0
DJClayworth

一般的に:

実装ではなくインターフェースを利用するソフトウェアを作成する必要があります。 「今」はインスタンス変数を使用しないと誰が言うでしょうが、将来は使用しますか?インターフェイスへのコーディングの例...

ArrayList badList = new ArrayList();  //bad
List goodList = new ArrayList();  //good

特にモックとテストのために、実装をスワップできるようにする必要があります。この点で、Spring Dependency Injectionは非常に優れています。 Springとbingoから実装を注入するだけで、ほぼ「静的」(まあ、シングルトン)メソッドがあります...

ここで、目的が純粋に「ユーティリティ」であるこれらのタイプのAPI(つまり、Apache Commons Lang)はここでは例外です。なぜなら、実装のほとんど(すべてではないにしても)は静的であると私は信じているからです。この状況で、Apache Commonsを別のAPIに交換する可能性はどれくらいありますか?

具体的には:

たとえば、Websphere対Tomcatデプロイメントを対象とする場合、実装の「静的」をエレガントに処理するにはどうすればよいでしょうか。私はあなたの実装が2つの間で異なる場合のインスタンス(意図されたしゃれはありません)があると確信しています...そしてそれらの特定の実装の1つで静的メソッドに依存することは危険かもしれません...

0
Tim Reddy