インターフェイスで静的メソッドを宣言できないのはなぜですか?
質問
このトピックでほとんどのことが述べられています。静的メソッドをインターフェイスで宣言できない理由は何ですか?
public interface ITest {
public static String test();
}
上記のコードでは、次のエラーが発生します (少なくとも Eclipse では)。「インターフェイス メソッド ITest.test() の修飾子が不正です。パブリックとアブストラクトのみが許可されます。」
解決
ここではいくつかの問題が関係しています。1 つ目は、静的メソッドを定義せずに宣言する問題です。これが違いです
public interface Foo {
public static int bar();
}
そして
public interface Foo {
public static int bar() {
...
}
}
前者は以下の理由から不可能です エスポ 言及:どの実装クラスが正しい定義であるかわかりません。
ジャワ できた 後者を許可します。実際、Java 8 以降ではそうなります。
他のヒント
インターフェイスに静的メソッドを含めることができない理由は、Java が静的参照を解決する方法にあります。Java は、静的メソッドを実行しようとするときに、わざわざクラスのインスタンスを探す必要はありません。これは、静的メソッドはインスタンスに依存しないため、クラス ファイルから直接実行できるためです。インターフェイス内のすべてのメソッドが抽象であるとすると、VM は、静的メソッドを実行できるようにその背後にあるコードを見つけるために、インターフェイスの特定の実装を探す必要があります。これは、静的メソッド解決の仕組みと矛盾し、言語に不整合をもたらすことになります。
あなたの質問に例を挙げて答えます。静的メソッド add を持つ Math クラスがあるとします。このメソッドは次のように呼び出します。
Math.add(2, 3);
Math がクラスではなくインターフェイスである場合、定義された関数を持つことはできません。したがって、Math.add(2, 3) のような記述は意味がありません。
その理由は、Java では多重継承が許可されていないという設計原則にあります。多重継承の問題は、次の例で説明できます。
public class A {
public method x() {...}
}
public class B {
public method x() {...}
}
public class C extends A, B { ... }
では、C.x() を呼び出すとどうなるでしょうか?A.x() または B.x() が実行されますか?多重継承を持つすべての言語は、この問題を解決する必要があります。
Java では、インターフェイスにより、ある種の制限された多重継承が可能になります。上記の問題を回避するために、メソッドを持つことは許可されません。インターフェイスと静的メソッドに関する同じ問題を見てみると、次のようになります。
public interface A {
public static method x() {...}
}
public interface B {
public static method x() {...}
}
public class C implements A, B { ... }
ここでも同じ問題があります。C.x() を呼び出すとどうなりますか?
静的メソッドはインスタンス メソッドではありません。インスタンス コンテキストがないため、インターフェイスから実装することはほとんど意味がありません。
Java8 では、インターフェイスで静的メソッドも定義できるようになりました。
interface X {
static void foo() {
System.out.println("foo");
}
}
class Y implements X {
//...
}
public class Z {
public static void main(String[] args) {
X.foo();
// Y.foo(); // won't compile because foo() is a Static Method of X and not Y
}
}
注記:Interface のメソッドは、default/static キーワードを明示的に使用してデフォルト メソッドと静的メソッドにしない限り、デフォルトではパブリック抽象のままです。
あなたの質問に対する非常に素晴らしく簡潔な答えがあります ここ. 。(非常にわかりやすく説明されていると感じたので、ここからリンクさせていただきます。)
インターフェイスの静的メソッドがサポートされている可能性があるようです Java 8, そうですね、私の解決策は、それらを内部クラスで定義するだけです。
interface Foo {
// ...
class fn {
public static void func1(...) {
// ...
}
}
}
同じテクニックをアノテーションでも使用できます。
public @interface Foo {
String value();
class fn {
public static String getValue(Object obj) {
Foo foo = obj.getClass().getAnnotation(Foo.class);
return foo == null ? null : foo.value();
}
}
}
内部クラスは常に次の形式でアクセスする必要があります。 Interface.fn...
の代わりに Class.fn...
, そうすれば、曖昧な問題を取り除くことができます。
インターフェイスはポリモーフィズムに使用され、型ではなくオブジェクトに適用されます。したがって、(すでに述べたように) 静的インターフェイス メンバーを持つことは意味がありません。
Java 8 ではインターフェースに静的メソッドを含めることができるように世界が変わりましたが、そのための実装を提供する必要がありました。
public interface StaticMethodInterface {
public static int testStaticMethod() {
return 0;
}
/**
* Illegal combination of modifiers for the interface method
* testStaticMethod; only one of abstract, default, or static permitted
*
* @param i
* @return
*/
// public static abstract int testStaticMethod(float i);
default int testNonStaticMethod() {
return 1;
}
/**
* Without implementation.
*
* @param i
* @return
*/
int testNonStaticMethod(float i);
}
修飾子の不正な組み合わせ:静的かつ抽象的
クラスのメンバーが静的として宣言されている場合、オブジェクトを作成せずに、そのクラスに限定されたクラス名で使用できます。
クラスのメンバーが抽象として宣言されている場合は、クラスを抽象として宣言し、その継承クラス (サブクラス) に抽象メンバーの実装を提供する必要があります。
静的メソッドの動作を変更するサブクラス内のクラスの抽象メンバーに実装を提供する必要がありますが、これも基本クラスに限定された抽象として宣言されていますが、これは正しくありません
静的メソッドは継承できないので。したがって、それをインターフェイスに配置しても無駄です。インターフェイスは基本的に、すべての加入者が従わなければならない契約です。インターフェイスに静的メソッドを配置すると、サブスクライバにそのメソッドの実装が強制されます。これは、静的メソッドを継承できないという事実と矛盾します。
と Java 8, 、インターフェイスに静的メソッドを含めることができるようになりました。
たとえば、Comparator には静的な NaturalOrder() メソッドがあります。
インターフェイスに実装を含めることができないという要件も緩和されました。インターフェイスは「デフォルト」メソッド実装を宣言できるようになりました。これは、1 つの例外を除いて通常の実装と似ています。インターフェイスからデフォルトの実装を継承し、スーパークラスから通常の実装を継承した場合は、スーパークラスの実装が常に優先されます。
おそらくコード例が役立つでしょう。ここでは C# を使用しますが、理解できるはずです。
IPayable というインターフェイスがあると仮定してみましょう
public interface IPayable
{
public Pay(double amount);
}
これで、このインターフェースを実装する 2 つの具体的なクラスができました。
public class BusinessAccount : IPayable
{
public void Pay(double amount)
{
//Logic
}
}
public class CustomerAccount : IPayable
{
public void Pay(double amount)
{
//Logic
}
}
ここで、さまざまなアカウントのコレクションがあると仮定してみましょう。これを行うには、IPayable タイプの汎用リストを使用します。
List<IPayable> accountsToPay = new List<IPayable>();
accountsToPay.add(new CustomerAccount());
accountsToPay.add(new BusinessAccount());
ここで、これらすべてのアカウントに $50.00 を支払いたいと考えます。
foreach (IPayable account in accountsToPay)
{
account.Pay(50.00);
}
これで、インターフェイスがいかに非常に便利であるかがわかりました。
これらはインスタンス化されたオブジェクトでのみ使用されます。静的クラスではありません。
pay を静的にした場合、accountsToPay で IPayable をループするときに、BusinessAcount または CustomerAccount のどちらで pay を呼び出す必要があるかを判断する方法がありません。