構文、抽象クラスに適用されるルールがわかっているので、抽象クラスの使い方を知りたい
抽象クラスは直接インスタンス化できませんが、他のクラスによって拡張できます
そうすることの利点は何ですか?
インターフェースとどう違うのですか?
1つのクラスで複数のインターフェースを実装できますが、拡張できるのは1つの抽象クラスのみです。それはインターフェースと抽象クラスの違いだけですか?
インターフェイスの使用方法を知っています。 JavaでのAWTのイベント委譲モデルからそれを学びました。
クラスを抽象クラスとして宣言する必要があるのはどのような場合ですか?その利点は何ですか?
This 回答は、抽象クラスとインターフェースの違いを説明するのに役立ちますが、回答しませんなぜ宣言する必要があるのか。
純粋に技術的な観点から見ると、クラスを抽象として宣言するための要件はありません。
次の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クラスを抽象化するhaveは行いません。このプログラムを作成しているときは、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
のみを実装する別個のクラスです。
類似性
抽象化には抽象クラスと抽象インターフェースが必要です。 newでインスタンス化することはできませんが、制御コンテナーの反転またはファクトリーパターンを介して解決することは可能です。
差
インターフェース
抽象クラス
simple google query で答えを見つけるのは実際には簡単です。
インターフェースとどう違うのですか?
抽象クラスでは、いくつかのメソッドを実装し、残りを拡張クラスが実装するように(強制)できます。インターフェイスにメソッドを実装することはできません。通常のクラスを拡張するときに、誰かに強制的に何かをオーバーライドすることはできません。抽象クラスがあればできます。
抽象クラスは「ある」関係用であり、インターフェースは「できる」ためのものです。
抽象クラスを使用すると、基本的な動作を追加できるため、プログラマーがすべてをコーディングする必要がなくなり、設計に準拠するように強制することができます。
深い技術的な詳細に加えて-抽象クラスのいくつかのメソッドの実装など、意味は次のとおりです:
インターフェイスは共通の機能を定義します-IEnumerableは、このインターフェイスを実装するクラスを列挙できることを定義します。クラス自体については何も述べていません。
抽象(または基本)クラスは動作を定義します-WebRequestはHttpWebRequestなどのすべての子クラスの共通の動作を定義します。Webリソースにアクセスすることは、クラスのコアの意味と実際の目的を定義します。
インターフェースと抽象クラスの主な違いは、抽象クラスが実装されたメソッドを提供できることです。インターフェースでは、declareメソッドのみを宣言し、それらの署名を書き込むことができます。 2つのインターフェースを実装する抽象クラスを拡張するクラスの例を以下に示します。(Java)
interface MyInterface1 {
string getValue1();
}
interface MyInterface2 {
string getValue2();
}
abstract class MyAbstractClass implements MyInterface1, MyInterface2{
void printValues() {
System.out.println("Value 1: " + getValue1() + ", Value 2: " + getValue2() +
", Value 3: " + getValue3());
}
protected abstract string getValue3();
}
class ImpClass extends MyAbstractClass {
public string getValue1() {
return "1";
}
public string getValue2() {
return "2";
}
protected string getValue3() {
return "3";
}
}
この例では、MyAbstractClassは、3つの値すべてを出力するパブリックメソッドを提供します。 ImpClassでは、getValue1およびgetValue2をそれぞれMyInterface1およびMyInterface2およびgetValue3から実装する必要があります抽象クラス。
ボイラ。
より多くの側面があります(インターフェース:パブリックメソッドのみ、抽象クラス:保護された抽象メソッドおよびパブリック抽象メソッド)。
最後に、抽象メソッドのみを提供する抽象クラスは、「純粋な」抽象基本クラス、つまりインターフェースです。
つまり、「これらのクラスは必ず実装を共有するか、または共通のインターフェース? "
これらの3つのクラスが実装を共有する必要があるが、他の2つはAPIのみを共有するなど、答えが混在している場合、5つすべてのインターフェイスを作成し、これら3つのクラスの抽象クラスを共通で作成できます。コード。
たとえば、オブジェクトをその実装でカプセル化するなど、実装を共有する他の方法もあります(例: Strategy パターン)。
開発者(おそらく自分自身)にインスタンス化を許可したくない場合は、クラスの抽象を宣言します。これは、機能しないか、意味がないためです。
たとえば、さまざまな種類のゲームエンティティが存在するゲームについて考えます。それらはすべて基本GameEntity
クラスから継承します。
_abstract class GameEntity{
int lifePoint, speed, damage;
public attack(GameEntity target){ target.damage(damage); }
public damage(int damageInflicted){ lifePoint -= damageInflicted - speed; }
// etc...
}
_
このクラスは、インスタンス化しても意味がないため、abstract
と宣言されています。ゲームエンティティのいくつかのアクションといくつかの属性を宣言しますが、このクラスのどこにもこれらの属性が初期化されていません。このクラスはゲームエンティティのテンプレートとして機能しますが、それ自体でインスタンス化されることを意図していないため、abstract
として宣言されています。
抽象クラスとインターフェースの使用法の違いについて:
私が見ているように、インターフェイスは、一部の言語の単一継承メカニズムに制限されることなく、ポリモーフィックな動作を実現する方法です。
例としてゲームに戻りましょう。 Enemy
から派生したクラスGameEntity
を考えます。このクラスには、メソッドattackMeFromDistance(RangedAttacker attacker)
があります。この方法は、エンティティが遠くから敵を攻撃できるようにするためのものです。
ご覧のとおり、このメソッドはRangedAttacker
型をパラメーターとして受け取ります。ただし、すべてのゲームエンティティは既にGameEntity
を継承しています。別のクラスを拡張することはできません。
たとえば、クラスMage
とArcher
を考えてみましょう。これらの両方をattackMeFromDistance(RangedAttacker attacker)
メソッドのパラメーターとして受け入れられるようにしたいのですが、それらはすでにGameEntity
から派生しています。
これを解決するために、新しいインターフェイスを作成します。
_interface RangedAttacker{
public void attackFromDistance();
}
_
このインターフェースを実装するクラスはattackFromDistance()
メソッドを実装する必要があるため、広範囲の攻撃機能があることが保証されます。つまり、attackMeFromDistance
メソッドは、このインターフェースを実装するクラスを安全に受け入れることができるようになりました。したがって、Mage
とArcher
にそのインターフェースを実装させると、問題が解決します。
私にとって、これはインターフェースの力です。
つまり、いくつかのクラスの基本クラスが必要な場合は、通常、抽象クラスを使用しますが、それ自体でインスタンス化しても意味がありません(またはabstract
がある場合)。サブクラスで実装する必要があるメソッド。この場合、コンパイラーはクラスをabstract
にすることを強制します。単一継承メカニズムに制限されることなく、インターフェースを使用して多態的な動作を実現します。