web-dev-qa-db-ja.com

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

これがその例です。

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)静的コンストラクターがあることを常に知っているでしょう。

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

編集: Java 8以降、静的メソッドはインタフェースで使用できるようになりました。

461
cdmckay

Java 8は静的インターフェースメソッドを許可します

Java 8では、インターフェイスに静的メソッドを含めることができます。また、具象インスタンスメソッドを持つこともできますが、インスタンスフィールドは持ちません。

ここには本当に2つの質問があります。

  1. なぜ、古き悪しき時代に、インターフェイスに静的メソッドを含めることができなかったのですか?
  2. 静的メソッドをオーバーライドできないのはなぜですか?

インターフェイスの静的メソッド

以前のバージョンでは、インターフェイスに静的メソッドを使用できなかった強力な技術的理由はありませんでした。これは 投稿者がうまくまとめた 重複した質問です。静的インターフェイスメソッドは、最初は 言語の小さな変更 と見なされ、その後Java 7に追加する 公式の提案 がありましたが、後で行われました 予期しない合併症のためにドロップされました。

最後に、Java 8では、静的インターフェイスメソッドと、既定の実装でオーバーライド可能なインスタンスメソッドが導入されました。ただし、インスタンスフィールドを保持することはできません。これらの機能はラムダ式サポートの一部であり、詳細については JSR 335のパートH を参照してください。

静的メソッドのオーバーライド

2番目の質問への答えはもう少し複雑です。

静的メソッドはコンパイル時に解決可能です。動的ディスパッチは、インスタンスメソッドの場合に意味があります。このメソッドでは、コンパイラはオブジェクトの具体的な型を判断できないため、呼び出すメソッドを解決できません。ただし、静的メソッドを呼び出すにはクラスが必要です。そのクラスはstaticallyであることがわかっているため、コンパイル時に動的なディスパッチは不要です。

ここで何が起こっているのかを理解するには、インスタンスメソッドがどのように機能するかについての少しの背景が必要です。実際の実装はかなり異なると確信していますが、観察された動作を正確にモデル化するメソッドディスパッチの概念を説明しましょう。

各クラスには、メソッドシグネチャ(名前とパラメーターの種類)を実際のコードチャンクにマッピングしてメソッドを実装するハッシュテーブルがあると仮定します。仮想マシンがインスタンスのメソッドを呼び出そうとすると、オブジェクトのクラスを照会し、クラスのテーブルで要求された署名を検索します。メソッド本体が見つかった場合、それが呼び出されます。それ以外の場合、クラスの親クラスが取得され、そこで検索が繰り返されます。これは、メソッドが見つかるか、親クラスがなくなるまで続行し、結果としてNoSuchMethodErrorになります。

スーパークラスとサブクラスの両方に同じメソッドシグネチャのテーブルにエントリがある場合、サブクラスのバージョンが最初に検出され、スーパークラスのバージョンは使用されません。これは「オーバーライド」です。

ここで、オブジェクトインスタンスをスキップして、サブクラスから開始するとします。解決は上記のように進められ、一種の「オーバーライド可能な」静的メソッドを提供します。ただし、コンパイラは実行時にクラスの不特定型のオブジェクトを照会するのを待つのではなく、既知のクラスから開始するため、解決はすべてコンパイル時に行われます。目的のバージョンを含むクラスをいつでも指定できるため、静的メソッドを「オーバーライド」しても意味がありません。


コンストラクター「インターフェース」

質問の最近の編集に対処するための資料がもう少しあります。

IXMLizableの各実装に対して、コンストラクターに似たメソッドを効果的に指定したいようです。インターフェースを使用してこれを1分間強制することを忘れ、この要件を満たすクラスがあると仮定します。どのように使用しますか?

class Foo implements IXMLizable<Foo> {
  public static Foo newInstanceFromXML(Element e) { ... }
}

Foo obj = Foo.newInstanceFromXML(e);

新しいオブジェクトを「構築」するときに、具体的な型に明示的に名前を付ける必要があるため、Fooコンパイラは、必要なファクトリメソッドが実際にあることを確認できます。そして、そうでない場合、それで何? 「コンストラクター」のないIXMLizableを実装でき、インスタンスを作成してコードに渡すと、それはisan IXMLizable必要なすべてのインターフェイス。

構築は実装の一部であり、インターフェイスではありません。インターフェイスで正常に動作するコードは、コンストラクターを気にしません。とにかくコンストラクターを気にするコードは具象型を知る必要があり、インターフェースは無視できます。

494
erickson

これはすでに尋ねられて答えられました、 ここ

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

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

さらに重要なことに、静的メソッドは決してオーバーライドされません。

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

staticの規則は、宣言された型のvarで定義されたメソッドは実行されなければならないと言います。これはインターフェースなので、これは不可能です。

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

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

46
DJClayworth

通常これはFactoryパターンを使って行われます。

public interface IXMLizableFactory<T extends IXMLizable> {
  public T newInstanceFromXML(Element e);
}

public interface IXMLizable {
  public Element toXMLElement();
}
36
Peter Lawrey

Java 8 の出現により、インターフェイスに default および static メソッドを記述することが可能になりました。 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

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

33
rogue lad

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

19
Michael Myers

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

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

Javaによると doc

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

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

デフォルトの方法の例:

list.sort(ordering);

の代わりに

Collections.sort(list, ordering);

静的メソッドの例( doc 自分から):

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));
    }    
}
10
i_am_zero

まず、言語に関するすべての決定は、言語の作成者によって行われた決定です。ソフトウェア工学や言語定義、あるいはコンパイラ/インタプリタの記述の世界には、静的メソッドをインタフェースの一部にすることはできないと言うものは何もありません。私はいくつかの言語を作成し、それらのためにコンパイラを書きました - それはすべてただ座って意味のあるセマンティクスを定義することです。たとえコンパイラーがメソッドの解決を実行時に遅らせる必要があったとしても、インターフェース内の静的メソッドのセマンティクスは非常に明確であると私は主張します。

第二に、静的メソッドを使用するということは、静的メソッドを含むインターフェースパターンを使用することには正当な理由があるということです。

最も可能性の高い正しい答えは、言語が定義された時点では、インターフェース内の静的メソッドに対する認識された必要性はなかったということです。 Javaは長年にわたって大きく成長してきましたが、これは明らかに関心を集めている項目です。それがJava 7のために見られたということは言語の変化をもたらすかもしれない興味のレベルにそれが上がったことを示します。 1つは、サブクラスインスタンスの静的変数にアクセスするために非静的getterメソッドを呼び出すことができるようにオブジェクトをインスタンス化する必要がなくなったときにうれしいことです。

6
tallgirl
  • msgstr "静的メソッドをオーバーライドできないという特別な理由がありますか?".

定義を記入することによってあなたのためにその質問をもう一度言いましょう。

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

あるいは、もっと完全に言えば、インスタンスなしでメソッドを呼び出したいが、クラスを知っている場合、自分が持っていないインスタンスに基づいてそれをどのように解決させることができますか。

5
Darron

インターフェイスは、クラスではなくオブジェクトインスタンスに本質的に結び付けられている多態性に関係しています。したがって、staticはインターフェースの文脈では意味がありません。

5
cliff.meyers

静的メソッドはインスタンスメソッドのように仮想的ではないので、私はJavaデザイナが彼らがインタフェースにそれらを望まないと決めたと思います。

しかし、静的メソッドを含むクラスをインタフェースの中に置くことができます。あなたはそれを試すことができます!

public interface Test {
    static class Inner {
        public static Object get() {
            return 0;
        }
    }
}
5
Adrian Pronk

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

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

3
flavio

ジェネリックがないと、静的メソッドは役に立ちません。静的メソッド呼び出しはすべてコンパイル時に解決されるからです。だから、それらのための本当の用途はありません。

総称では、デフォルトの実装の有無にかかわらず使用できます。明らかに上書きなどが必要になるでしょう。ただし、そのような使用法はOOではない(他の回答では曖昧に指摘されているように)ため、実用的に実装するために必要な努力に見合う価値があるとは考えられませんでした。

2
MichaelGG

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

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

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

iDでPersonをロードする

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

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

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

Why can't I define a static method in a Java interface?

インタフェース内のすべてのメソッドは明示的に抽象的であり、静的メソッドは抽象的にはできないため、静的メソッドとして定義することはできません。

2
Aniket Thakur

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

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

したがって、インタフェース内の静的メソッドに最も近い近似値を取得するには、非静的メソッドを使用してから、非静的インスタンスメンバにアクセスしないようにします。 ISomething.member()を(コンパイル時に)静的にリンクする方法がないため、他の方法でパフォーマンス上の利点はありません。私がインターフェース内の静的メソッドから見た唯一の利点は、暗黙の "this"を入力(つまり無視)しないため、非静的インスタンスメンバーへのアクセスが許可されないことです。これは、「this」にアクセスしない関数は不変であり、その包含クラスに関して読み取り専用でさえないことを暗黙のうちに宣言します。しかし、インターフェースISomethingで "static"と宣言すると、ISomething.member()でアクセスしようとした人々を混乱させ、コンパイラエラーを引き起こします。私はここでやっているように、コンパイラエラーが十分に説明的であるなら、それは彼らが欲しいものを達成するために非静的メソッド(明らかにファクトリメソッド)を使うことについて人々を教育することよりも良いでしょう。このサイトでQ&Aを行っていますので、それは明らかに多くの人にとって直観的ではない問題です。私は正しい理解を得るためにしばらく考えなければなりませんでした。

インターフェース内で可変静的フィールドを取得する方法は、サブクラス内の静的フィールドにアクセスするために、インターフェース内で非静的getterメソッドおよびsetterメソッドを使用することです。傍注、明らかに不変の統計はstatic finalを使ってJavaインターフェースで宣言することができます。

1

実装できるものは、(インターフェース内の静的メソッドではなく)静的インターフェースです。特定の静的インタフェースを実装するすべてのクラスは、対応する静的メソッドを実装する必要があります。あなたはを使用して任意のクラスクラスから静的インタフェースSIを得ることができます

SI si = clazz.getStatic(SI.class); // null if clazz doesn't implement SI
// alternatively if the class is known at compile time
SI si = Someclass.static.SI; // either compiler errror or not null

それならsi.method(params)を呼び出せます。コンパイル時に不明なクラスからSI静的メソッドの実装を取得(または実装の確認)できるので、これは(たとえばファクトリデザインパターンの場合に)便利です。動的ディスパッチが必要であり、クラスを拡張することで(静的インタフェースを介して呼び出されたときに)クラスの静的メソッド(最後ではない場合)をオーバーライドできます。明らかに、これらのメソッドはそのクラスの静的変数にしかアクセスできない。

0

静的メソッドがインターフェイスで許可されているとしましょう:*それらはすべての実装クラスにそのメソッドを宣言させるでしょう。 *インタフェースは通常オブジェクトを通して使われるので、それらに対する唯一の有効な方法は非静的なものでしょう。 *特定のインターフェースを知っているどのクラスでもその静的メソッドを呼び出すことができます。したがって、実装クラスの静的メソッドがその下で呼び出されることになりますが、呼び出し側クラスにはどちらがわかりません。それを知る方法は?それを推測するインスタンス化はありません!

オブジェクトを扱うときにインタフェースが使われると考えられていました。このようにして、オブジェクトは特定のクラスからインスタンス化されるので、この最後の問題は解決されます。呼び出し元のクラスは、インスタンス化が3番目のクラスによって行われる可能性があるため、どの特定のクラスかを知る必要はありません。そのため、呼び出し側クラスはインターフェースのみを知っています。

これを静的メソッドに拡張したい場合は、以前に実装クラスを指定してから呼び出し元クラスへの参照を渡す必要があります。これはインタフェースの静的メソッドを通してクラスを使うことができます。しかし、この参照とオブジェクトの違いは何ですか?それがクラスだったものを表すオブジェクトが必要です。これで、オブジェクトは古いクラスを表し、古い静的メソッドを含む新しいインタフェースを実装できるようになりました - これらは非静的になりました。

メタクラスはこの目的に役立ちます。あなたはクラスClass of Javaを試すことができます。しかし問題は、Javaがこれに対して十分に柔軟ではないということです。インターフェイスのクラスオブジェクト内でメソッドを宣言することはできません。

これはメタ問題です - あなたがお尻をする必要があるとき

.. blah blah

とにかく、あなたは簡単な回避策を持っています - 同じロジックでメソッドを非静的にすること。しかし、それからメソッドを呼び出すために最初にオブジェクトを作成しなければならないでしょう。

0
beibichunai

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

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

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

0
samoz

私は、Java 8がこの問題を解決していることを認識していますが、私が現在取り組んでいるシナリオ(Java 7を使うことに固執している)に慣れようと思いました。

私は様々な理由で値を評価するヘルパーメソッドと共に "id"と "displayName"フィールドを定義したいくつかの列挙型定義を持っています。インターフェイスを実装することで、getterメソッドは適切に配置されているが静的ヘルパーメソッドは配置されていないことを確認できます。 enumなので、ヘルパーメソッドを継承された抽象クラスなどにオフロードするための明確な方法は実際にはありません。そのため、メソッドはenum自体で定義する必要があります。それはenumなので、実際にそれをインスタンス化されたオブジェクトとして渡してインターフェース型として扱うことはできないでしょうが、インターフェースを通して静的ヘルパーメソッドの存在を要求することができるのは私が好きなものです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, " , ")));
    }
}
0
Ryan

静的メソッドはクラスのインスタンスではなくクラスに属し、インタフェースはクラスではないため、インタフェースに静的メソッドを定義することはできません。 もっと読む

ただし、必要に応じてこれを実行できます。

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

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

この場合、持っているのはmethodX()という2つの異なる静的メソッドを持つ2つのクラスです。

0
Handerson

あなたができるとしましょう。この例を考えてください。

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

}
0
pvgoddijn

これを解決するには、次のようにします。

interface I
{
    int x=20;
    void getValue();
    static void main(String[] args){};//Put curly braces 
}
class InterDemo implements I
{
    public void getValue()
    {
    System.out.println(x);
    }
    public static void main(String[] args)
    {
    InterDemo i=new InterDemo();
    i.getValue();   
    }

}

出力:20

これでインターフェースで静的メソッドを使用できます

0
ashish pawar