この答えは、抽象クラスとインターフェースの違いを説明するのに良い仕事をしますが、なぜ宣言する必要があるのかは答えません。
純粋に技術的な観点からは、クラスを抽象として宣言する必要はありません。
次の3つのクラスを検討してください。
class Database {
public String[] getTableNames() { return null; } //or throw an exception? who knows...
}
class SqlDatabase extends Database { } //TODO: override getTableNames
class OracleDatabase extends Database { } //TODO: override getTableNames
実装に明らかな問題がありますが、Databaseクラスを抽象化する必要はありません。このプログラムを作成しているときに、入力new Database()
して有効にすることはできますが、機能しません。
かかわらず、あなたはまだあなたのプログラムだけになり、長いほどとして、多形になるだろうSqlDatabase
し、OracleDatabase
インスタンスを、あなたのようなメソッドを記述することができます。
public void printTableNames(Database database) {
String[] names = database.getTableNames();
}
抽象クラスは、開発者が基本クラスをインスタンス化できないようにすることで状況を改善します。これは、開発者が基本クラスが欠落しているとマークしたためです。また、コンパイル時の安全性も提供するため、抽象クラスを拡張するクラスが最小限の機能を提供することを保証でき、継承者が何らかの形で持っているスタブメソッド(上記のような)を置くことを心配する必要はありませんメソッドを機能させるためにメソッドをオーバーライドする必要があることを魔法のように知っています。
インターフェイスはまったく別のトピックです。インターフェイスを使用すると、オブジェクトに対して実行できる操作を記述できます。通常、他のコンポーネント、オブジェクトのサービスを使用するメソッド、コンポーネントなどを作成するときはインターフェイスを使用しますが、サービスの取得元のオブジェクトの実際のタイプは気にしません。
次の方法を検討してください。
public void saveToDatabase(IProductDatabase database) {
database.addProduct(this.getName(), this.getPrice());
}
database
オブジェクトが特定のオブジェクトを継承するかどうかは気にせず、addProduct
メソッドを持っているだけです。したがって、この場合、すべてのクラスを同じベースクラスから継承させるよりも、インターフェイスの方が適しています。
時々、2つの組み合わせは非常にうまく機能します。例えば:
abstract class RemoteDatabase implements IProductDatabase {
public abstract String[] connect();
public abstract void writeRow(string col1, string col2);
public void addProduct(String name, Double price) {
connect();
writeRow(name, price.toString());
}
}
class SqlDatabase extends RemoteDatabase {
//TODO override connect and writeRow
}
class OracleDatabase extends RemoteDatabase {
//TODO override connect and writeRow
}
class FileDatabase implements IProductDatabase {
public void addProduct(String name, Double price) {
//TODO: just write to file
}
}
データベースの一部がRemoteDatabaseから継承して機能を共有する方法(行を書き込む前に接続するなど)に注意してください。ただし、FileDatabaseはを実装するだけの別個のクラスですIProductDatabase
。