トピックはそれのほとんどを述べています-静的メソッドがインターフェイスで宣言できないという事実の理由は何ですか?
public interface ITest {
public static String test();
}
上記のコードを実行すると、次のエラーが発生します(少なくともEclipseでは)。
トピックはそれのほとんどを述べています-静的メソッドがインターフェイスで宣言できないという事実の理由は何ですか?
public interface ITest {
public static String test();
}
上記のコードを実行すると、次のエラーが発生します(少なくともEclipseでは)。
回答:
ここには、いくつかの問題があります。1つ目は、静的メソッドを定義せずに宣言する問題です。これは
public interface Foo {
public static int bar();
}
そして
public interface Foo {
public static int bar() {
...
}
}
Espoが言及している理由により、1つ目は不可能です。どの実装クラスが正しい定義であるかがわかりません。
Java は後者を許可できます。そして実際、Java 8以降では、そうなります!
static
では、メソッドをで定義できますinterface
。メソッドはでなければなりませんpublic
。
インターフェースに静的メソッドを含めることができない理由は、Javaが静的参照を解決する方法にあります。Javaは、静的メソッドを実行しようとするときに、クラスのインスタンスを探す必要はありません。これは、静的メソッドはインスタンスに依存しないため、クラスファイルから直接実行できるためです。インターフェースのすべてのメソッドが抽象的であることを考えると、VMは、静的メソッドの背後にあるコードを見つけて実行できるようにするために、インターフェースの特定の実装を探す必要があります。これは、静的メソッド解決がどのように機能するかと矛盾し、言語に不整合をもたらします。
その理由は、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()を呼び出すとどうなりますか?
A
が含まれていてint x(int z);
、インターフェースB
が含まれているよりも悪い状況はありますstring x(int x);
か?x(3)
インターフェイスCの意味は何ですか?
静的メソッドはインスタンスメソッドではありません。インスタンスコンテキストがないため、インターフェイスから実装することはほとんど意味がありません。
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を明示的に使用してそれらをデフォルトメソッドおよび静的メソッドにそれぞれ指定しない場合、インターフェースのメソッドはデフォルトで引き続きパブリック抽象です。
ここにあなたの質問に対する非常に素晴らしく簡潔な答えがあります。(ここからリンクしたいという、とてもわかりやすい説明の仕方で私を驚かせました。)
インターフェースの静的メソッドは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、インターフェースは、今の静的メソッドを持つことができます。
たとえば、コンパレータにはstatic 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を呼び出す必要があるかどうかを判断する方法はありません。