私はJavaのインターフェースとクラス継承についてたくさん読んでいて、両方を行う方法を知っているので、両方に良い感じがあると思います。しかし、実際に2つを並べて比較して、いつ、なぜ使用したいのかを説明する人はいないようです。スーパークラスを拡張するよりもインターフェイスを実装する方が優れたシステムになることは多くありません。
では、いつインターフェースを実装し、いつスーパークラスを拡張するのでしょうか?
コントラクトを定義したい場合は、インターフェースを使用します。つまりXはYを受け取り、Zを返す必要があります。コードがそれを行っている方法は関係ありません。クラスはmultipleインターフェースを実装できます。
デフォルトの動作を非で定義したい場合は、抽象クラスを使用しますエンドユーザーが何度も書き換えることなく再利用できるように、抽象メソッド。クラスは、1つだけ他のクラスから拡張できます。 only抽象メソッドを含む抽象クラスは、インターフェイスと同じように定義できます。抽象メソッドのない抽象クラスは Template Method パターンとして認識されます(実際の例については この答え を参照してください) 。
抽象クラスは、エンドユーザーがデフォルトの動作を自由に定義できるようにしたい場合はいつでも、インターフェースを完全に実装できます。
インターフェースを選択する必要があります(契約を定義する(つまり、継承クラスに実装するメソッドシグネチャ)インターフェースはまったく実装されていない場合があります。継承するクラスは、独自の実装を自由に選択できます。
部分的な実装を基本型で定義し、残りを継承クラスに任せたい場合があります。その場合は、抽象クラスを選択してください。抽象クラスは、メソッドの実装と変数を定義しながら、一部のメソッドを抽象として残すことができます。拡張クラスは、抽象メソッドの実装方法を選択できますが、スーパークラスによって提供される部分的な実装もあります。
抽象クラスの極値の1つは、純粋な抽象クラスです。抽象クラスだけで他には何もありません。 純粋な抽象クラスとインターフェイスの関係の場合、インターフェイスを使用してになります。 Javaは単一の実装の継承のみを許可しますが、複数のインターフェースの継承は許可しますが、クラスは複数のインターフェースを実装できますが、拡張できるのは1つのクラスのみです。したがって、インターフェースよりも純粋な抽象クラスを選択すると、サブクラスが抽象メソッドの実装中に他のクラスを拡張することはできません。
define動作へのインターフェースを使用します。 実装を提供するユーザー(抽象)クラス(およびサブクラス)。これらは相互に排他的ではありません。彼らはすべて一緒に働くことができます。
たとえば、データアクセスオブジェクトを定義しているとします。 DAOがデータをロードできるようにしたい。したがって、インターフェースにloadメソッドを配置します。これは、DAOを呼び出したいものはすべてロードを実装する必要があることを意味します。ここで、AとBをロードする必要があるとしましょう。パラメーター化されたジェネリック抽象クラス(ジェネリック)を作成して、ロードの動作方法の概要を提供できます。次に、その抽象クラスをサブクラス化して、AおよびBの具象実装を提供します。
抽象クラスとインターフェースを使用する主な理由は異なります。
一連のメソッドの実装が同一であるが、いくつか異なるクラスがある場合、抽象クラスを使用する必要があります。
これは悪い例かもしれませんが、Javaフレームワークでの抽象クラスの最も明らかな使用法は、Java.ioクラス内にあります。OutputStream
は、バイトのストリームにすぎません。 OutputStream
のどのサブクラスを使用しているか... FileOutputStream
、PipedOutputStream
、_Java.net.Socket
_のgetOutputStream
メソッドから作成された出力ストリーム...
注:Java.ioは、他のストリーム/リーダー/ライターでストリームをラップするために、Decoratorパターンも使用します。
クラスが一連のメソッドを実装することを保証するだけで、その方法は気にしない場合は、インターフェースを使用する必要があります。
インターフェイスの最も明らかな使用法は、コレクションフレームワーク内です。
add(something)
とget(0)
を呼び出して要素を配置および取得できる限り、List
が要素を追加/削除する方法は気にしません。配列(ArrayList
、CopyOnWriteArrayList
)、リンクリスト(LinkedList
)などを使用できます。
インターフェースを使用するもう1つの利点は、クラスが複数を実装できることです。 LinkedList
は、List
andDeque
の両方の実装です。
誰も?
http://mindprod.com/jgloss/interfacevsabstract.html
編集:私はリンク以上のものを提供する必要があります
ここに状況があります。以下の車の例を基に、これを検討してください
interface Drivable {
void drive(float miles);
}
abstract class Car implements Drivable {
float gallonsOfGas;
float odometer;
final float mpg;
protected Car(float mpg) { gallonsOfGas = 0; odometer = 0; this.mpg = mpg; }
public void addGas(float gallons) { gallonsOfGas += gallons; }
public void drive(float miles) {
if(miles/mpg > gallonsOfGas) throw new NotEnoughGasException();
gallonsOfGas -= miles/mpg;
odometer += miles;
}
}
class LeakyCar extends Car { // still implements Drivable because of Car
public addGas(float gallons) { super.addGas(gallons * .8); } // leaky tank
}
class ElectricCar extends Car {
float electricMiles;
public void drive(float miles) { // can we drive the whole way electric?
if(electricMiles > miles) {
electricMiles -= miles;
odometer += miles;
return; // early return here
}
if(electricMiles > 0) { // exhaust electric miles first
if((miles-electricMiles)/mpg > gallonsOfGas)
throw new NotEnoughGasException();
miles -= electricMiles;
odometer += electricMiles;
electricMiles = 0;
}
// finish driving
super.drive(miles);
}
}
インターフェイスは、オブジェクトが特定のプロパティまたは動作を持ち、複数の継承ツリーにまたがっており、各クラスに対して明確に定義されているだけであることを表現するために使用する場合に最も効果的に機能すると思います。
たとえば、比較可能と考えてください。他のクラスによって拡張されるComparableクラスを作成する場合は、継承ツリーで非常に高くする必要があります。Objectの直後に可能であり、そのクラスが表すプロパティは、そのタイプの2つのオブジェクトを比較できることですが、一般的にそれを定義する方法はありません(comparableクラスでcompareToの実装を直接持つことはできません。それを実装するクラスごとに異なります)。
クラスは、明確なものを定義し、クラスにどのようなプロパティと動作があり、子に受け渡したいメソッドの実際の実装がある場合に最もよく機能します。
したがって、人間や車などの具象オブジェクトを定義する必要がある場合はクラスが機能し、比較(比較可能)や比較などの機能のように、継承ツリーに属すには一般的すぎる抽象的な動作が必要な場合は、インターフェースがより適切に機能します実行されます(実行可能)。
interface
とbase class
のどちらかを選択する1つの方法は、コードの所有権を考慮することです。すべてのコードを制御する場合、基本クラスは実行可能なオプションです。一方、多くの異なる企業が交換可能なコンポーネントを生産したい場合、つまり契約を定義の場合、インターフェースが唯一の選択肢です。
派生クラスが同じ型である場合、スーパークラスからの拡張と考えることができます。つまり、クラスが抽象クラスを拡張する場合、それらは両方とも同じ型である必要があります。唯一の違いは、スーパークラスにはより多くの一般的な動作とサブクラスには、より具体的な動作があります。インターフェースはまったく異なる概念です。クラスがインターフェースを実装するとき、それはいくつかのAPI(contract)を公開するか、または特定の動作を取得します。例として、Carは抽象クラスであると言います。 FordやChevyなど、それぞれのタイプの車のような多くのクラスを拡張できます。しかし、たとえば車にGPSが必要だと言うような特定の動作が必要な場合は、具体的なクラス、たとえばフォードはGPSインターフェースを実装する必要があります。
私はいくつかの記事を見つけました、特にあなたが実装継承を使わないほうがいい理由を説明している記事(すなわちスーパークラス)を見つけました:
クラシックカーの例を挙げましょう。
車のインターフェースがある場合は、フォード、シェビー、オールズモビルを作成できます。つまり、車のインターフェースからさまざまな種類の車を作成します。
車のクラスがある場合は、車のクラスを拡張して、トラックまたはバスを作成できます。つまり、基本クラスまたはスーパークラスの属性を維持したまま、サブクラスに新しい属性を追加します。
サブクラスでメソッドシグネチャ(名前、引数、戻り値の型)のみを継承する場合はインターフェイスを使用しますが、実装コードも継承する場合はスーパークラスを使用します。