トピックはそれのほとんどを述べています-静的メソッドがインターフェースで宣言できないという事実の理由は何ですか?
public interface ITest {
public static String test();
}
上記のコードでは、(少なくともEclipseでは)次のエラーが発生します。「インターフェイスメソッドITest.test()の修飾子が無効です。publicとabstractのみが許可されます」。
ここでいくつかの問題が発生します。1 つ目は、静的メソッドを定義せずに宣言するという問題です。これが違いです
public interface Foo {
public static int bar();
}
と
public interface Foo {
public static int bar() {
...
}
}
1 つ目は、 Espoが言及している理由により不可能です。つまり、どの実装クラスが正しい定義であるかがわかりません。
Javaは後者を許可できます。実際、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 { ... }
Cx() を呼び出すとどうなるでしょうか。Ax() または Bx() が実行されますか? 多重継承を持つすべての言語は、この問題を解決する必要があります。
インターフェイスは、Java である種の制限された多重継承を許可します。上記の問題を回避するために、メソッドを持つことは許可されていません。インターフェイスと静的メソッドで同じ問題を見ると、次のようになります。
public interface A {
public static method x() {...}
}
public interface B {
public static method x() {...}
}
public class C implements A, B { ... }
ここでも同じ問題があります。Cx() を呼び出すとどうなりますか?
静的メソッドはインスタンスメソッドではありません。インスタンスコンテキストがないため、インターフェイスから実装することはほとんど意味がありません。
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
}
}
注: キーワード default/static を明示的に使用して Default メソッドと Static メソッドをそれぞれ作成しない限り、Interface のメソッドはデフォルトでパブリック抽象のままです。
ここにあなたの質問に対する非常に素晴らしく簡潔な答えがあります。(それは、ここからリンクしたい説明のとても簡単な方法だと思いました。)
インターフェイスの静的メソッドは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);
}
これで、インターフェイスが非常に便利であることがわかりました。
それらは、インスタンス化されたオブジェクトでのみ使用されます。静的クラスではありません。
支払いを静的にした場合、accountsToPay で IPayable をループするときに、BusinessAcount または CustomerAccount で支払いを呼び出す必要があるかどうかを判断する方法がありません。