41

私はこのコードでそれを理解しています:

class Foo {
    public static void method() {
        System.out.println("in Foo");
    }
} 

class Bar extends Foo {
    public static void method() {
        System.out.println("in Bar");
    }
}

.. の静的メソッドは、ポリモーフィズムの意味でオーバーライドするのではなく、 でBar宣言された静的メソッドを「非表示」にします。Foo

class Test {
    public static void main(String[] args) {
        Foo.method();
        Bar.method();
    }
}

...出力します:

イン フー
イン バー

method()asfinalを再定義すると、それを非表示にFooする機能が無効にBarなり、再実行main()すると次のように出力されます。

イン・フー・
イン・フー

(編集:メソッドを としてマークするとコンパイルが失敗し、final削除したときにのみ再度実行されますBar.method())

finalサブクラスが意図的または不注意にメソッドを再定義するのを止める場合、静的メソッドを として宣言することは悪い習慣と見なされますか?

これは、使用の動作が何であるかの良い説明finalです..)

4

10 に答える 10

40

staticメソッドを としてマークするのは悪い習慣だとは思いませんfinal

あなたが知ったfinalように、メソッドがサブクラスによって隠されるのを防ぎます。これは非常に良いニュースです

私はあなたの声明に非常に驚いています:

Foo で method() を final として再定義すると、Bar がそれを非表示にする機能が無効になり、main() を再実行すると次のように出力されます。

イン・フー・
イン・フー

いいえ、メソッドをfinalinとしてマークすると、コンパイルFooできなくなります。Bar少なくとも Eclipse では次のようになります。

スレッド「メイン」の例外 java.lang.Error: 未解決のコンパイルの問題: Foo からの最終メソッドをオーバーライドできません

staticまた、クラス自体の中でもクラス名で修飾するメソッドを常に呼び出す必要があると思います。

class Foo
{
  private static final void foo()
  {
    System.out.println("hollywood!");
  }

  public Foo()
  {
    foo();      // both compile
    Foo.foo();  // but I prefer this one
  }
}
于 2009-12-19T09:03:57.333 に答える
35

静的メソッドは、Java の最も紛らわしい機能の 1 つです。これを修正するためのベスト プラクティスがあります。すべての静的メソッドを作成することfinalは、これらのベスト プラクティスの 1 つです。

静的メソッドの問題は、

  • これらはクラス メソッドではなく、クラス名の前に付けられたグローバル関数です。
  • それらがサブクラスに「継承」されているのは奇妙です
  • それらをオーバーライドすることはできず、非表示にすることは驚くべきことです
  • インスタンスをレシーバーとして呼び出すことができるのは完全に壊れています

したがって、あなたはすべきです

  • 常にクラスをレシーバとして呼び出します
  • 宣言クラスのみをレシーバーとして常に呼び出します
  • 常にそれらを作成します(または宣言クラス)final

そして、あなたはすべきです

  • インスタンスをレシーバーとして呼び出してはいけません
  • 宣言クラスのサブクラスをレシーバーとして呼び出してはいけません
  • サブクラスでそれらを再定義しないでください

 

注意:プログラムの 2 番目のバージョンでは、コンパイル エラーが発生するはずです。あなたのIDEはこの事実を隠していると思います!

于 2009-12-19T21:36:10.333 に答える
7

メソッドがある場合、それは多くの場合、メソッドのみを持つpublic staticいわゆるユーティリティ クラスstaticに既に配置されています。自明の例はStringUtil、、、SqlUtilなどIOUtilです。これらのユーティリティ クラスは、それ自体ですでに宣言されており、コンストラクターfinalで提供されています。private例えば

public final class SomeUtil {

    private SomeUtil() {
        // Hide c'tor.
    }

    public static SomeObject doSomething(SomeObject argument1) {
        // ...
    }

    public static SomeObject doSomethingElse(SomeObject argument1) {
        // ...
    }

}

この方法では、それらをオーバーライドできません。

あなたのものがユーティリティクラスのようなものではない場合、public修飾子の値に疑問があります。そうではないprivateでしょうか?それ以外の場合は、それをいくつかのユーティリティ クラスに移動します。public staticメソッドで「通常の」クラスを乱雑にしないでください。この方法では、それらをマークする必要もありませんfinal

public staticもう 1 つのケースは、メソッドを介して self の具体的な実装を返す一種の抽象ファクトリ クラスです。そのような場合、メソッドをマークすることは完全に理にかなってfinalいます。具体的な実装でメソッドをオーバーライドできるようにしたくありません。

于 2009-12-19T21:11:11.417 に答える
4

通常、ユーティリティ クラス (静的メソッドのみを持つクラス) では、継承を使用することは望ましくありません。このため、クラスを final として定義して、他のクラスがそれを拡張しないようにすることができます。これにより、ユーティリティ クラス メソッドに final 修飾子を配置する必要がなくなります。

于 2009-12-19T15:28:55.400 に答える
3

コードはコンパイルされません:

Test.java:8: Bar の method() は Foo の method() をオーバーライドできません。オーバーライドされたメソッドは static final public static void method() {

静的メソッドは定義によりオーバーライドできないため、このメッセージは誤解を招きます。

私はコーディング時に次のことを行います (常に 100% ではありませんが、「間違っている」ものは何もありません:

(「ルール」の最初のセットは、ほとんどの場合に実行されます - いくつかの特殊なケースは後でカバーされます)

  1. インターフェイスを作成する
  2. インターフェイスを実装する抽象クラスを作成する
  3. 抽象クラスを拡張する具象クラスを作成する
  4. インターフェイスを実装するが抽象クラスを拡張しない具象クラスを作成する
  5. 常に、可能であれば、インターフェイスのすべての変数/定数/パラメーターを作成します

インターフェイスには静的メソッドを含めることができないため、問題が発生することはありません。抽象クラスまたは具象クラスで静的メソッドを作成する場合、それらはプライベートにする必要があり、それらをオーバーライドしようとする方法はありません。

特殊なケース:

ユーティリティ クラス (すべての静的メソッドを持つクラス):

  1. クラスを final として宣言する
  2. 偶発的な作成を防ぐためにプライベート コンストラクターを指定する

非公開の具象クラスまたは抽象クラスに静的メソッドが必要な場合は、代わりにユーティリティ クラスを作成することをお勧めします。

値クラス (x 値と y 値をほとんど保持している java.awt.Point のように、本質的にデータを保持するために非常に特殊化されたクラス):

  1. インターフェイスを作成する必要はありません
  2. 抽象クラスを作成する必要はありません
  3. クラスは final にする必要があります
  4. プライベートでない静的メソッドは問題ありません。特に、キャッシュを実行したい場合があるため、構築には問題ありません。

上記のアドバイスに従うと、かなり柔軟なコードになり、責任もかなり明確に分離されます。

値クラスの例は、次の Location クラスです。

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public final class Location
    implements Comparable<Location>
{
    // should really use weak references here to help out with garbage collection
    private static final Map<Integer, Map<Integer, Location>> locations;

    private final int row;    
    private final int col;

    static
    {
        locations = new HashMap<Integer, Map<Integer, Location>>();
    }

    private Location(final int r,
                     final int c)
    {
        if(r < 0)
        {
            throw new IllegalArgumentException("r must be >= 0, was: " + r);
        }

        if(c < 0)
        {
            throw new IllegalArgumentException("c must be >= 0, was: " + c);
        }

        row = r;
        col = c;
    }

    public int getRow()
    {
        return (row);
    }

    public int getCol()
    {
        return (col);
    }

    // this ensures that only one location is created for each row/col pair... could not
    // do that if the constructor was not private.
    public static Location fromRowCol(final int row,
                                      final int col)
    {
        Location               location;
        Map<Integer, Location> forRow;

        if(row < 0)
        {
            throw new IllegalArgumentException("row must be >= 0, was: " + row);
        }

        if(col < 0)
        {
            throw new IllegalArgumentException("col must be >= 0, was: " + col);
        }

        forRow = locations.get(row);

        if(forRow == null)
        {
            forRow = new HashMap<Integer, Location>(col);
            locations.put(row, forRow);
        }

        location = forRow.get(col);

        if(location == null)
        {
            location = new Location(row, col);
            forRow.put(col, location);
        }

        return (location);
    }

    private static void ensureCapacity(final List<?> list,
                                       final int     size)
    {
        while(list.size() <= size)
        {
            list.add(null);
        }
    }

    @Override
    public int hashCode()
    {
        // should think up a better way to do this...
        return (row * col);
    }

    @Override
    public boolean equals(final Object obj)
    {
        final Location other;

        if(obj == null)
        {
            return false;
        }

        if(getClass() != obj.getClass())
        {
            return false;
        }

        other = (Location)obj;

        if(row != other.row)
        {
            return false;
        }

        if(col != other.col)
        {
            return false;
        }

        return true;
    }

    @Override
    public String toString()
    {
        return ("[" + row + ", " + col + "]");
    }

    public int compareTo(final Location other)
    {
        final int val;

        if(row == other.row)
        {
            val = col - other.col;
        }
        else
        {
            val = row - other.row;
        }

        return (val);
    }
}
于 2009-12-19T18:03:40.873 に答える
1

特に、他の人が拡張することを期待するフレームワークを開発している場合は、静的メソッドをfinalとしてマークすることをお勧めします。そうすれば、ユーザーが誤って静的メソッドをクラスに隠してしまうことはありません。ただし、フレームワークを開発している場合は、最初から静的メソッドの使用を避けたい場合があります。

于 2009-12-19T17:04:26.237 に答える
1

この問題のほとんどはfinal、VM が非常に愚かで保守的だった時代にさかのぼります。当時、メソッドをマークした場合、final(とりわけ) メソッドの呼び出しを回避して、VM がそれをインライン化できることを意味していました。長い間 (または長い double :P ) 時間があるため、そうではありません: http://java.sun.com/developer/technicalArticles/Networking/HotSpot/inlining.html

Idea/ Netbeansインスペクションは、最適化のためにキーワードを使用したいと考えておりfinal、最新の VM では不要であるという事実を認識していないと考えているため、警告していると思います。

ちょうど私の2セント...

于 2012-07-02T22:38:12.100 に答える
0

静的メソッドはクラスのプロパティであり、オブジェクトではなくクラスの名前で呼び出されるためです。親クラス メソッドも final にすると、final メソッドはそのメモリ位置を変更できないためオーバーロードされませんが、同じメモリ位置で最終データ メンバーを更新できます...

于 2013-07-21T03:55:09.600 に答える
0

純粋なユーティリティ クラスを作成するときは、拡張できないようにプライベート コンストラクターを使用して宣言します。通常のクラスを作成するとき、メソッドがクラス インスタンス変数を使用していない場合は、メソッドを static と宣言します (または、場合によっては、使用していたとしても、メソッドに引数を渡して静的にするので、見やすくなります)。メソッドが何をしているのか)。これらのメソッドは静的であると宣言されていますが、非公開でもあります。コードの重複を避けるため、またはコードを理解しやすくするためだけに存在します。

そうは言っても、パブリック静的メソッドを持ち、拡張できる/拡張する必要があるクラスがある場合に遭遇したことを覚えていません。しかし、ここで報告されたことに基づいて、静的メソッドを final と宣言します。

于 2009-12-19T20:34:55.000 に答える
0

Spring の AOP と MVC を使用して final メソッドを使用すると、1 つの不利益が生じました。final と宣言された AbstractFormController のメソッドの 1 つにセキュリティ フックを配置して、Spring の AOP を使用しようとしていました。春はクラスでの注入にbcelライブラリを使用していたと思いますが、そこにはいくつかの制限がありました。

于 2009-12-19T18:21:08.280 に答える