web-dev-qa-db-ja.com

クラスを抽象クラスとして宣言する必要があるのはなぜですか?

構文、抽象クラスに適用されるルールがわかっているので、抽象クラスの使い方を知りたい

抽象クラスは直接インスタンス化できませんが、他のクラスによって拡張できます

そうすることの利点は何ですか?

インターフェースとどう違うのですか?

1つのクラスで複数のインターフェースを実装できますが、拡張できるのは1つの抽象クラスのみです。それはインターフェースと抽象クラスの違いだけですか?

インターフェイスの使用方法を知っています。 JavaでのAWTのイベント委譲モデルからそれを学びました。

クラスを抽象クラスとして宣言する必要があるのはどのような場合ですか?その利点は何ですか?

40
Vaibhav Jani

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のみを実装する別個のクラスです。

50
Kevin McCormick

類似性

抽象化には抽象クラスと抽象インターフェースが必要です。 newでインスタンス化することはできませんが、制御コンテナーの反転またはファクトリーパターンを介して解決することは可能です。


  1. インターフェース

    • よく知られている公共契約、タイプの能力を定義する
    • 水平継承、つまり最初のレベルの継承での分岐を表示するのに適用可能(例:データベースへのロギング機能を定義するILog、テキストファイル、XML、SOAPなど))
    • すべてのメンバーは公開されています
    • 実装は許可されていません
    • 継承の子は実装する多くのインターフェースを持つことができます
    • サードパーティの統合に役立ちます
    • 命名は通常[〜#〜] i [〜#〜]で始まります
  2. 抽象クラス

    • 構造、ID、およびデフォルトでサポートされているいくつかの動作を定義する
    • 垂直継承、つまり複数のレベルでの深い分岐(ドメイン駆動開発のAbstractEntityクラスなど)を示すのに適用可能
    • メンバーは(パブリックからプライベートに)異なる可視性を持つことができます
    • 一部のメンバー(* Readerクラスなど)を実装できます
    • 継承の子は基本抽象クラスを1つだけ持つことができます

simple google query で答えを見つけるのは実際には簡単です。

16
oleksii

インターフェースとどう違うのですか?

抽象クラスでは、いくつかのメソッドを実装し、残りを拡張クラスが実装するように(強制)できます。インターフェイスにメソッドを実装することはできません。通常のクラスを拡張するときに、誰かに強制的に何かをオーバーライドすることはできません。抽象クラスがあればできます。

11
Joonas Pulakka

抽象クラスは「ある」関係用であり、インターフェースは「できる」ためのものです。

抽象クラスを使用すると、基本的な動作を追加できるため、プログラマーがすべてをコーディングする必要がなくなり、設計に準拠するように強制することができます。

8

深い技術的な詳細に加えて-抽象クラスのいくつかのメソッドの実装など、意味は次のとおりです:

インターフェイスは共通の機能を定義します-IEnumerableは、このインターフェイスを実装するクラスを列挙できることを定義します。クラス自体については何も述べていません。

抽象(または基本)クラスは動作を定義します-WebRequestはHttpWebRequestなどのすべての子クラスの共通の動作を定義します。Webリソースにアクセスすることは、クラスのコアの意味と実際の目的を定義します。

3
Sunny

ウィキペディアエントリ

インターフェースと抽象クラスの主な違いは、抽象クラスが実装されたメソッドを提供できることです。インターフェースでは、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から実装する必要があります抽象クラス。

ボイラ。

より多くの側面があります(インターフェース:パブリックメソッドのみ、抽象クラス:保護された抽象メソッドおよびパブリック抽象メソッド)。

最後に、抽象メソッドのみを提供する抽象クラスは、「純粋な」抽象基本クラス、つまりインターフェースです。

2
Jalayn
  • インターフェース-いくつかのクラスがAPI(メソッド名とパラメーター)を共有する場合
  • 抽象クラス-いくつかのクラスが同じコードを共有する場合(実装)

つまり、「これらのクラスは必ず実装を共有するか、または共通のインターフェース? "

これらの3つのクラスが実装を共有する必要があるが、他の2つはAPIのみを共有するなど、答えが混在している場合、5つすべてのインターフェイスを作成し、これら3つのクラスの抽象クラスを共通で作成できます。コード。

たとえば、オブジェクトをその実装でカプセル化するなど、実装を共有する他の方法もあります(例: Strategy パターン)。

2
Viliam Búr

開発者(おそらく自分自身)にインスタンス化を許可したくない場合は、クラスの抽象を宣言します。これは、機能しないか、意味がないためです。

たとえば、さまざまな種類のゲームエンティティが存在するゲームについて考えます。それらはすべて基本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を継承しています。別のクラスを拡張することはできません。

たとえば、クラスMageArcherを考えてみましょう。これらの両方をattackMeFromDistance(RangedAttacker attacker)メソッドのパラメーターとして受け入れられるようにしたいのですが、それらはすでにGameEntityから派生しています。

これを解決するために、新しいインターフェイスを作成します。

_interface RangedAttacker{
    public void attackFromDistance();
}
_

このインターフェースを実装するクラスはattackFromDistance()メソッドを実装する必要があるため、広範囲の攻撃機能があることが保証されます。つまり、attackMeFromDistanceメソッドは、このインターフェースを実装するクラスを安全に受け入れることができるようになりました。したがって、MageArcherにそのインターフェースを実装させると、問題が解決します。

私にとって、これはインターフェースの力です。

つまり、いくつかのクラスの基本クラスが必要な場合は、通常、抽象クラスを使用しますが、それ自体でインスタンス化しても意味がありません(またはabstractがある場合)。サブクラスで実装する必要があるメソッド。この場合、コンパイラーはクラスをabstractにすることを強制します。単一継承メカニズムに制限されることなく、インターフェースを使用して多態的な動作を実現します。

1
Aviv Cohn
  1. 一部のクラス(ビジネスロジック)に共通するメソッドが少なすぎる可能性があります。残りの方法は異なります。そのようなシナリオでは、1つのクラスにすべての一般的なメソッドを実装し、残りを抽象として宣言できます。次に、クラスを抽象として宣言する必要があります。
  2. クラスにオブジェクトを直接作成できない場合があります。クラスを抽象として宣言する必要があるそのようなクラス。
0
Dharani Kumar