540

編集: Java 8 の時点で、インターフェイスで静的メソッドが許可されるようになりました。

次に例を示します。

public interface IXMLizable<T>
{
  static T newInstanceFromXML(Element e);
  Element toXMLElement();
}

もちろん、これはうまくいきません。しかし、なぜですか?

考えられる問題の 1 つは、次のように呼び出すとどうなるかです。

IXMLizable.newInstanceFromXML(e);

この場合、空のメソッド (つまり {}) を呼び出すだけでよいと思います。すべてのサブクラスは静的メソッドの実装を強制されるため、静的メソッドを呼び出すときはすべて問題ありません。では、なぜこれが不可能なのですか?

編集:「Javaはそうなので」よりも深い答えを探していると思います。

静的メソッドを上書きできない特定の技術的な理由はありますか? つまり、なぜ Java の設計者は、インスタンス メソッドをオーバーライド可能にしたが、静的メソッドは作成しないことにしたのでしょうか?

編集:私の設計の問題は、インターフェイスを使用してコーディング規則を適用しようとしていることです。

つまり、インターフェースの目的は 2 つあります。

  1. IXMLizable インターフェイスを使用して、それを実装するクラスを XML 要素に変換できるようにしたいと考えています (ポリモーフィズムを使用すると問題なく動作します)。

  2. 誰かが IXMLizable インターフェイスを実装するクラスの新しいインスタンスを作成したい場合、newInstanceFromXML(Element e) 静的コンストラクターがあることを常に知っています。

インターフェイスにコメントを入れる以外に、これを確実にする方法はありますか?

4

24 に答える 24

52

これはすでに質問され、ここで答えられました

私の答えを複製するには:

インターフェイスで静的メソッドを宣言する意味はありません。通常の呼び出しMyInterface.staticMethod()では実行できません。実装クラスMyImplementor.staticMethod()を指定してそれらを呼び出す場合は、実際のクラスを知っている必要があるため、インターフェイスにクラスが含まれているかどうかは関係ありません。

さらに重要なことに、静的メソッドがオーバーライドされることはありません。

MyInterface var = new MyImplementingClass();
var.staticMethod();

staticのルールでは、宣言された型のvarで定義されたメソッドを実行する必要があるとされています。これはインターフェースであるため、これは不可能です。

「result=MyInterface.staticMethod()」を実行できない理由は、MyInterfaceで定義されたバージョンのメソッドを実行する必要があるためです。ただし、MyInterfaceはインターフェイスであるため、バージョンを定義することはできません。定義上、コードはありません。

これは「Javaがそのように行うため」に相当すると言えますが、実際には、この決定は他の設計上の決定の論理的帰結であり、これも非常に正当な理由で行われます。

于 2009-02-04T19:50:18.367 に答える
40

Java 8の登場により、インターフェイスに デフォルトメソッドと静的メソッドを記述できるようになりました。docs.oracle/staticMethod

例えば:

public interface Arithmetic {

    public int add(int a, int b);

    public static int multiply(int a, int b) {
        return a * b;
    }
}
public class ArithmaticImplementation implements Arithmetic {

    @Override
    public int add(int a, int b) {
        return a + b;
    }

    public static void main(String[] args) {
        int result = Arithmetic.multiply(2, 3);
        System.out.println(result);
    }
}

結果: 6

ヒント :静的インターフェイス メソッドの呼び出しは、クラスによって実装される必要はありません。確かに、これは、スーパークラスの静的メソッドと同じ規則がインターフェイスの静的メソッドに適用されるために発生します。

于 2014-11-26T17:13:17.233 に答える
22

静的メソッドはサブクラスでオーバーライドできないため、抽象化できません。そして、インターフェイスのすべてのメソッドは、事実上、抽象的です。

于 2009-02-04T19:22:59.047 に答える
13

Java インターフェイスで静的メソッドを定義できないのはなぜですか?

実際、Java 8ではできます。

Java docに従って:

静的メソッドは、任意のオブジェクトではなく、それが定義されているクラスに関連付けられているメソッドです。クラスのすべてのインスタンスは、その静的メソッドを共有します

Java 8 では、インターフェイスはデフォルト メソッド静的メソッドを持つことができます。これにより、ライブラリでヘルパー メソッドを整理しやすくなります。別のクラスではなく、同じインターフェイス内のインターフェイスに固有の静的メソッドを保持できます。

デフォルトの方法の例:

list.sort(ordering);

それ以外の

Collections.sort(list, ordering);

静的メソッドの例 (ドキュメント自体から):

public interface TimeClient {
    // ...
    static public ZoneId getZoneId (String zoneString) {
        try {
            return ZoneId.of(zoneString);
        } catch (DateTimeException e) {
            System.err.println("Invalid time zone: " + zoneString +
                "; using default time zone instead.");
            return ZoneId.systemDefault();
        }
    }

    default public ZonedDateTime getZonedDateTime(String zoneString) {
        return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }    
}
于 2015-07-01T13:33:05.697 に答える
6

インターフェイスは、本質的にクラスではなくオブジェクト インスタンスに関連付けられているポリモーフィズムに関係しています。したがって、静的はインターフェイスのコンテキストでは意味がありません。

于 2009-02-04T19:28:41.297 に答える
5
  • 「静的メソッドをオーバーライドできない特定の理由はありますか」.

定義を記入して、その質問を言い換えさせてください。

  • 「コンパイル時に解決されたメソッドが実行時に解決できない特定の理由はありますか?」

または、より完全に言えば、インスタンスなしでメソッドを呼び出したいが、クラスを知っている場合、持っていないインスタンスに基づいて解決するにはどうすればよいですか。

于 2009-02-04T21:16:21.203 に答える
4

コメントEDIT: As of Java 8, static methods are now allowed in interfaces.

Java 8以降の静的メソッドはインターフェースで許可されていますが、あなたの例はまだ機能しません。静的メソッドを定義することはできません。実装する必要があります。そうしないと、コンパイル エラーが発生します。

于 2016-06-15T10:29:18.640 に答える
3

いくつかの回答は、オーバーライド可能な静的メソッドの概念に関する問題について説明しています。しかし、それがまさにあなたが使いたいものであるように見えるパターンに出くわすことがあります。

たとえば、値オブジェクトを含むオブジェクトリレーショナルレイヤーを操作しますが、値オブジェクトを操作するためのコマンドもあります。さまざまな理由から、各値オブジェクトクラスは、フレームワークがコマンドインスタンスを検出できるようにするいくつかの静的メソッドを定義する必要があります。たとえば、Personを作成するには、次のようにします。

cmd = createCmd(Person.getCreateCmdId());
Person p = cmd.execute();

IDでPersonをロードするには

cmd = createCmd(Person.getGetCmdId());
cmd.set(ID, id);
Person p = cmd.execute();

これはかなり便利ですが、問題があります。特に、静的メソッドの存在をインターフェイスに強制することはできません。インターフェイスでオーバーライド可能な静的メソッドは、それが何らかの形で機能する場合にのみ、まさに必要なものになります。

EJBは、ホームインターフェイスを使用することでこの問題を解決します。各オブジェクトはそのホームを見つける方法を知っており、ホームには「静的」メソッドが含まれています。このようにして、「静的」メソッドを必要に応じてオーバーライドでき、Beanのインスタンスに適用されないメソッドで通常の(「リモート」と呼ばれる)インターフェースを乱雑にすることはありません。通常のインターフェースで「getHome()」メソッドを指定するだけです。Homeオブジェクトのインスタンス(シングルトンの可能性があります)を返すと、呼び出し元はすべてのPersonオブジェクトに影響を与える操作を実行できます。

于 2009-02-04T20:40:48.093 に答える
2

ジェネリックがなければ、すべての静的メソッド呼び出しはコンパイル時に解決されるため、静的インターフェイスは役に立ちません。したがって、それらの実際の使用はありません。

ジェネリックを使用すると、デフォルトの実装の有無にかかわらず使用できます。明らかに、オーバーライドなどが必要になります。ただし、私の推測では、そのような使用法はあまりOOではなく(他の回答が鈍く指摘しているように)、したがって、有用に実装するために必要な労力に見合う価値があるとは見なされませんでした。

于 2009-02-04T19:27:39.940 に答える
1

インターフェイスは、静的に逆参照することはできませんISomething.member。インターフェイスは、インターフェイスのサブクラスのインスタンスを参照する変数を介して常に逆参照されます。したがって、インターフェイス参照は、そのサブクラスのインスタンスがなければ、参照するサブクラスを知ることはできません。

したがって、インターフェイスの静的メソッドに最も近いものは、「this」を無視する非静的メソッドです。つまり、インスタンスの非静的メンバーにはアクセスしません。低レベルの抽象化では、すべての非静的メソッド (任意の vtable でのルックアップ後) は、実際には "this" を暗黙的な仮パラメーターとして受け取るクラス スコープを持つ単なる関数です。その概念の証拠として、Scala のシングルトン オブジェクトと Java との相互運用性を参照してください。したがって、すべての静的メソッドは、"this" パラメーターを取らないクラス スコープを持つ関数です。したがって、通常、静的メソッドは静的に呼び出すことができますが、前述のように、インターフェイスには実装がありません (抽象的です)。

したがって、インターフェイスの静的メソッドに最も近いものを取得するには、非静的メソッドを使用し、非静的インスタンス メンバーにアクセスしないようにします。(コンパイル時に) ISomething.member(). インターフェイスの静的メソッドの唯一の利点は、暗黙的な「this」を入力しない (つまり無視する) ため、非静的インスタンス メンバーへのアクセスが許可されないことです。これは、「this」にアクセスしない関数が不変であり、それを含むクラスに関して読み取り専用でさえないことを暗黙的に宣言します。しかし、インターフェイスで「静的」を宣言するISomethingと、それにアクセスしようとした人々を混乱させることにもなります。ISomething.member()これにより、コンパイラ エラーが発生します。コンパイラ エラーが十分に説明的である場合、私たちがここで行っているように、非静的メソッド (明らかにほとんどがファクトリ メソッド) を使用して目的を達成することについて人々を教育しようとするよりも優れていると思います (3 年間繰り返されています)。このサイトの Q&A タイムズ)、多くの人にとって直感的ではない問題であることは明らかです。正しい理解を得るために、しばらく考えなければなりませんでした。

インターフェイスで変更可能な静的フィールドを取得する方法は、インターフェイスで非静的 getter および setter メソッドを使用して、サブクラス内の静的フィールドにアクセスすることです。補足として、どうやら不変の statics は、Java インターフェースで を使用して宣言できますstatic final

于 2011-02-14T12:22:47.460 に答える
0

それができるとしましょう。次の例を検討してください。

interface Iface {
  public static void thisIsTheMethod();
}

class A implements Iface {

  public static void thisIsTheMethod(){
    system.out.print("I'm class A");
  }

}

class B extends Class A {

  public static void thisIsTheMethod(){
    System.out.print("I'm class B");
  } 
}

SomeClass {

  void doStuff(Iface face) {
    IFace.thisIsTheMethod();
    // now what would/could/should happen here.
  }

}
于 2009-02-13T16:04:34.410 に答える
0

Java 8 がこの問題を解決することはわかっていますが、私が現在取り組んでいる (Java 7 の使用にロックされている) シナリオでは、インターフェイスで静的メソッドを指定できると便利だと思いました。

さまざまな理由で値を評価するヘルパー メソッドとともに、「id」フィールドと「displayName」フィールドを定義した列挙型定義がいくつかあります。インターフェースを実装することで、静的ヘルパー メソッドではなくゲッター メソッドが配置されていることを確認できます。列挙型であるため、ヘルパー メソッドを継承された抽象クラスなどにオフロードするクリーンな方法は実際には存在しないため、メソッドを列挙型自体で定義する必要があります。また、列挙型であるため、実際にインスタンス化されたオブジェクトとして渡してインターフェース型として扱うことはできませんが、インターフェースを介して静的ヘルパー メソッドの存在を要求できることは、私が気に入っていることです。 Java 8 でサポートされています。

これが私のポイントを示すコードです。

インターフェース定義:

public interface IGenericEnum <T extends Enum<T>> {
    String getId();
    String getDisplayName();
    //If I was using Java 8 static helper methods would go here
}

1 つの列挙定義の例:

public enum ExecutionModeType implements IGenericEnum<ExecutionModeType> {
    STANDARD ("Standard", "Standard Mode"),
    DEBUG ("Debug", "Debug Mode");

    String id;
    String displayName;

    //Getter methods
    public String getId() {
        return id;
    }

    public String getDisplayName() {
        return displayName;
    }

    //Constructor
    private ExecutionModeType(String id, String displayName) {
        this.id = id;
        this.displayName = displayName;
    }

    //Helper methods - not enforced by Interface
    public static boolean isValidId(String id) {
        return GenericEnumUtility.isValidId(ExecutionModeType.class, id);
    }

    public static String printIdOptions(String delimiter){
        return GenericEnumUtility.printIdOptions(ExecutionModeType.class, delimiter);
    }

    public static String[] getIdArray(){
        return GenericEnumUtility.getIdArray(ExecutionModeType.class);
    }

    public static ExecutionModeType getById(String id) throws NoSuchObjectException {
        return GenericEnumUtility.getById(ExecutionModeType.class, id);
    }
}

一般的な enum ユーティリティの定義:

public class GenericEnumUtility {
    public static <T extends Enum<T> & IGenericEnum<T>> boolean isValidId(Class<T> enumType, String id) {       
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(enumOption.getId().equals(id)) {
                return true;
            }
        }

        return false;
    }

    public static <T extends Enum<T> & IGenericEnum<T>> String printIdOptions(Class<T> enumType, String delimiter){
        String ret = "";
        delimiter = delimiter == null ? " " : delimiter;

        int i = 0;
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(i == 0) {
                ret = enumOption.getId();
            } else {
                ret += delimiter + enumOption.getId();
            }           
            i++;
        }

        return ret;
    }

    public static <T extends Enum<T> & IGenericEnum<T>> String[] getIdArray(Class<T> enumType){
        List<String> idValues = new ArrayList<String>();

        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            idValues.add(enumOption.getId());
        }

        return idValues.toArray(new String[idValues.size()]);
    }

    @SuppressWarnings("unchecked")
    public static <T extends Enum<T> & IGenericEnum<T>> T getById(Class<T> enumType, String id) throws NoSuchObjectException {
        id = id == null ? "" : id;
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(id.equals(enumOption.getId())) {
                return (T)enumOption;
            }
        }

        throw new NoSuchObjectException(String.format("ERROR: \"%s\" is not a valid ID. Valid IDs are: %s.", id, printIdOptions(enumType, " , ")));
    }
}
于 2015-05-20T19:10:56.913 に答える
0

静的メソッドはクラスのインスタンスではなくクラスに属し、インターフェイスはクラスではないため、インターフェイスで静的メソッドを定義することはできません。詳しくはこちらをご覧ください。

ただし、必要に応じてこれを行うことができます:

public class A {
  public static void methodX() {
  }
}

public class B extends A {
  public static void methodX() {
  }
}

この場合、methodX() と呼ばれる 2 つの異なる静的メソッドを持つ 2 つのクラスがあります。

于 2009-02-04T19:35:55.557 に答える
0

インターフェイスは、クラスが提供するもののリストを提供するだけであり、それらの実際の実装ではありません。これが静的アイテムです。

静的が必要な場合は、抽象クラスを使用して継承します。それ以外の場合は、静的を削除します。

それが役立つことを願っています!

于 2009-02-04T19:23:34.473 に答える
-2

あなたがそれらを必要としないので、私はjavaが静的なインターフェースメソッドを持っていないと思います。あなたはそう思うかもしれませんが...あなたはそれらをどのように使用しますか?あなたがそれらを次のように呼びたいなら

MyImplClass.myMethod()

その後、インターフェイスで宣言する必要はありません。あなたがそれらを次のように呼びたいなら

myInstance.myMethod()

その後、静的であってはなりません。実際に最初の方法を使用するが、各実装にそのような静的メソッドを強制したい場合、それは実際にはコーディング規約であり、インターフェイスを実装するインスタンスと呼び出し元のコードの間のコントラクトではありません。

インターフェイスを使用すると、インターフェイスを実装するクラスのインスタンスと呼び出し元のコードの間のコントラクトを定義できます。また、Javaは、このコントラクトに違反していないことを確認するのに役立ちます。そのため、javaに依存して、どのクラスがこのコントラクトを実装するかを心配する必要はありません。「コントラクトに署名した人」だけで十分です。静的インターフェースの場合、コード

MyImplClass.myMethod()

各インターフェイスの実装にこのメソッドがあるという事実に依存していないため、Javaを使用して確実に処理する必要はありません。

于 2009-02-04T20:35:31.800 に答える
-5

インターフェイスでの静的メソッドの必要性は何ですか。静的メソッドは基本的に、オブジェクトのインスタンスを作成する必要がない場合に使用されます。インターフェイスの全体的なアイデアは、概念から逸脱している静的メソッドの導入でOOPの概念を取り入れることです。

于 2013-04-04T11:06:52.610 に答える