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


40

私は構文、抽象クラスに適用されるルールを知っており、抽象クラスの使用法を知りたい

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

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

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

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

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

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


14
これは尋ねられました、あなたは知っています。検索すると、このような他の質問が表示されます。Googleで開始する必要があります。これにより、スタックオーバーフローにつながります。これらはすべて重複しています:stackoverflow.com/search?q=abstract+interface
S.Lott

1
「彼らが議論するだけの抽象クラスの使用...ルール」。何?「ルール」と「使用法」の違いは何ですか?ところで、あなたの正確な質問が尋ねられました。知っている。答えた。見続ける。検索の使用方法を学ぶことが重要です。
S.Lott

「どのような状況でクラスを抽象クラスとして宣言する必要がありますか?その利点は何ですか?」
バイバブジャニ

インターフェイスは純粋な抽象クラスであり、それだけです。それらは同じものです。サブクラスのみが持っているデータが必要なため、実装できない基本クラスの関数に対して常に抽象クラスを使用しますが、すべてのサブクラスがこの関数を持ち、それに応じて実装することを保証したいです。
AngryBird

テンプレートメソッドパターンは非常に強力で、抽象クラスの優れたユースケースであると思います。
m3th0dman

回答:


49

この答えは、抽象クラスとインターフェースの違いを説明するのに良い仕事をしますが、なぜ宣言する必要があるのかは答えません

純粋に技術的な観点からは、クラスを抽象として宣言する必要はありません。

次の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


16

類似点

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


  1. インターフェース

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

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

実際に簡単なグーグルクエリで答えを見つけるのは簡単です。


実装によって許可されないメナは何ですか?javaクラスはインターフェースを実装できます。
-Sajuuk

@Sajuukは、その行がインターフェイスを指すことを示します。コントラクトの実装をインターフェイスに配置することはできません。抽象クラスのコントラクトのデフォルト実装を使用できます。
oleksii

11

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

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


これには、デフォルトのメソッドが導入されたJava 8の更新が本当に必要です。
ホーコンLøtveit

8

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

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


3

技術的な詳細-抽象クラスなどのメソッドの実装など、詳細は次のとおりです。

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

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


2

ウィキペディアエントリ

インターフェイスと抽象クラスの主な違いは、抽象クラスが実装されたメソッドを提供できることです。インターフェイスを使用すると、メソッドのみを宣言し、その署名を記述できます。以下は、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を実装する必要がありMyInterface1MyInterface2抽象クラスからとgetValue3。

ほら

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

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


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

言い換えれば、「これらのクラスは必ず実装を共有するのか、それとも単に共通のインターフェースを持っているのか?」という質問から始めるべきです。

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

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


1

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

たとえば、さまざまな種類のゲームエンティティがあるゲームを考えます。それらはすべて基本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メソッドがこのインターフェースを実装するクラスを安全に受け入れることができることを意味します。そのためMageArcherそのインターフェイスを作成して実装すると、問題が解決します。

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

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


0
  1. 一部のクラス(ビジネスロジック)に共通するメソッドが少なすぎる可能性があります。そして、残りの方法は異なります。このようなシナリオでは、1つのクラスにすべての共通メソッドを実装し、残りを抽象として宣言できます。次に、クラスを抽象として宣言する必要があります。
  2. クラスにオブジェクトを直接作成できない場合があります。そのようなクラスは、クラスを抽象として宣言する必要があります。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.