編集: Java 8 以降では、インターフェースで静的メソッドが許可されるようになりました。
次に例を示します。
public interface IXMLizable<T>
{
static T newInstanceFromXML(Element e);
Element toXMLElement();
}
もちろん、これはうまくいきません。しかし、なぜうまくいかないのでしょうか?
考えられる問題の 1 つは、次の呼び出しを行うと何が起こるかということです。
IXMLizable.newInstanceFromXML(e);
この場合、空のメソッド (つまり {}) を呼び出すだけでよいと思います。すべてのサブクラスは静的メソッドを実装するように強制されるため、静的メソッドを呼び出すときにはすべて正常に動作します。では、なぜこれができないのでしょうか?
編集:私は「Java はそういうものだから」という答えよりも深い答えを求めていると思います。
静的メソッドを上書きできない特別な技術的な理由があるのでしょうか? つまり、Java の設計者はなぜインスタンス メソッドは上書き可能にして、静的メソッドは上書きできないようにしたのでしょうか?
編集:私の設計の問題は、コーディング規則を強制するためにインターフェースを使用しようとしていることです。
つまり、インターフェースの目標は次の 2 つです。
IXMLizable インターフェイスを使用して、それを実装するクラスを XML 要素に変換できるようにしたいと考えています (ポリモーフィズムを使用して、問題なく動作します)。
IXMLizable インターフェイスを実装するクラスの新しいインスタンスを作成したい場合、newInstanceFromXML(Element e) 静的コンストラクターが存在することが常にわかります。
インターフェースにコメントを入れる以外に、これを保証する方法はあるでしょうか?
ベストアンサー1
Java 8では静的インターフェースメソッドが許可される
Java 8 では、インターフェースに静的メソッドを設定できます。また、具体的なインスタンス メソッドも設定できますが、インスタンス フィールドは設定できません。
ここでは実際に 2 つの質問があります。
- なぜ、昔はインターフェースに静的メソッドを含めることができなかったのでしょうか?
- 静的メソッドをオーバーライドできないのはなぜですか?
インターフェース内の静的メソッド
以前のバージョンではインターフェースに静的メソッドを持たせることができなかった技術的な理由は特にありませんでした。ポスターにうまくまとめられている重複した質問です。静的インターフェースメソッドは当初、小さな言語の変更、そして、公式提案Java 7で追加する予定でしたが、その後予期せぬ合併症のため中止されました。
最後に、Java 8では静的インターフェースメソッドと、デフォルト実装を持つオーバーライド可能なインスタンスメソッドが導入されました。ただし、インスタンスフィールドを持つことはできません。これらの機能はラムダ式サポートの一部であり、詳細については以下を参照してください。JSR 335 のパート H。
静的メソッドのオーバーライド
2 番目の質問に対する答えはもう少し複雑です。
静的メソッドはコンパイル時に解決できます。動的ディスパッチは、コンパイラがオブジェクトの具体的な型を判別できず、そのため呼び出すメソッドを解決できないインスタンス メソッドに適しています。ただし、静的メソッドを呼び出すにはクラスが必要であり、そのクラスはコンパイル時に静的にわかっているため、動的ディスパッチは不要です。
ここで何が起こっているかを理解するには、インスタンス メソッドがどのように動作するかについての背景を少し理解する必要があります。実際の実装はまったく異なると思いますが、観察された動作を正確にモデル化するメソッド ディスパッチの概念について説明させてください。
各クラスには、メソッド シグネチャ (名前とパラメータ タイプ) をメソッドを実装する実際のコード チャンクにマップするハッシュ テーブルがあるとします。仮想マシンがインスタンスでメソッドを呼び出そうとすると、そのクラスのオブジェクトを照会し、要求されたシグネチャをクラスのテーブルで検索します。メソッド本体が見つかった場合は、それが呼び出されます。それ以外の場合は、クラスの親クラスが取得され、そこで検索が繰り返されます。メソッドが見つかるか、親クラスがなくなるまで、この処理が続けられます。親クラスがなくなると、 になりますNoSuchMethodError
。
スーパークラスとサブクラスの両方のテーブルに同じメソッド シグネチャのエントリがある場合、サブクラスのバージョンが最初に検出され、スーパークラスのバージョンは使用されません。これが「オーバーライド」です。
ここで、オブジェクト インスタンスをスキップして、サブクラスから開始するとします。解決は上記のように進められ、一種の「オーバーライド可能な」静的メソッドが提供されます。ただし、コンパイラは、実行時まで待機してそのクラスの未指定のタイプのオブジェクトを照会するのではなく、既知のクラスから開始するため、解決はすべてコンパイル時に行われます。必要なバージョンを含むクラスを常に指定できるため、静的メソッドを「オーバーライド」しても意味がありません。
コンストラクタ「インターフェース」
質問に対する最近の編集に対応するために、もう少し資料を用意しました。
の各実装に対して、コンストラクターのようなメソッドを効果的に強制したいようですIXMLizable
。これをインターフェースで強制しようとすることはしばらく忘れて、この要件を満たすクラスがいくつかあると仮定します。どのように使用しますか?
class Foo implements IXMLizable<Foo> {
public static Foo newInstanceFromXML(Element e) { ... }
}
Foo obj = Foo.newInstanceFromXML(e);
新しいオブジェクトを「構築」するときに具体的な型を明示的に指定する必要があるためFoo
、コンパイラは、必要なファクトリ メソッドが実際に存在するかどうかを確認できます。存在しない場合でも、どうなりますか? IXMLizable
「コンストラクタ」のない を実装し、インスタンスを作成してコードに渡すと、必要なインターフェイスがすべて備わった になりますIXMLizable
。
構築は実装の一部であり、インターフェースの一部ではありません。インターフェースで正常に動作するコードは、コンストラクターを気にしません。コンストラクターを気にするコードは、とにかく具体的な型を知る必要があるため、インターフェースは無視できます。