web-dev-qa-db-ja.com

InterfaceクラスとAbstractクラスの違いをどのように説明したらよいですか?

私のインタビューの1つでは、 Interface Abstract class の違いを説明するように依頼されました。

これが私の返事です:

Javaインタフェースのメソッドは暗黙的に抽象的であり、実装はできません。 Java抽象クラスは、デフォルトの動作を実装するインスタンスメソッドを持つことができます。

Javaインターフェースで宣言された変数は、デフォルトではfinalです。抽象クラスは、最終的ではない変数を含むことがあります。

Javaインタフェースのメンバは、デフォルトでパブリックです。 Java抽象クラスは、private、protectedなどのクラスメンバーの通常のフレーバーを持つことができます。

Javaインターフェースは、キーワード「implements」を使用して実装する必要があります。 Java抽象クラスは、キーワード「extends」を使用して拡張する必要があります。

インタフェースは他のJavaインタフェースのみを拡張でき、抽象クラスは他のJavaクラスを拡張して複数のJavaインタフェースを実装できます。

Javaクラスは複数のインタフェースを実装できますが、拡張できる抽象クラスは1つだけです。

しかし、インタビュアーは満足しておらず、この記述は "bookish knowledge"を表していると私に言った。

彼は私にもっと実用的な応答を求めて、{実際的な例を使って、インターフェースより抽象クラスを選ぶときを説明した。

どこで私は間違えましたか?

439
Thinker

最初に例を挙げます。

public interface LoginAuth{
   public String encryptPassword(String pass);
   public void checkDBforUser();
}

アプリケーションに3つのデータベースがあるとします。それから、そのデータベースのすべての実装は、上記の2つのメソッドを定義する必要があります。

public class DBMySQL implements LoginAuth{
          // Needs to implement both methods
}
public class DBOracle implements LoginAuth{
          // Needs to implement both methods
}
public class DBAbc implements LoginAuth{
          // Needs to implement both methods
}

しかし、encryptPassword()がデータベースに依存しておらず、各クラスで同じであるとしたらどうでしょうか。それでは上記は良いアプローチではないでしょう。

代わりに、この方法を検討してください。

public abstract class LoginAuth{
   public String encryptPassword(String pass){
            // Implement the same default behavior here 
            // that is shared by all subclasses.
   }

   // Each subclass needs to provide their own implementation of this only:
   public abstract void checkDBforUser();
}

各子クラスでは、1つのメソッド(データベースに依存するメソッド)を実装するだけで済みます。

私は全力を尽くしたし、これがあなたの疑問を解決することを願っています。

485
Vimal Bera

この世で完璧なものは何もない。彼らはもっと実用的なアプローチを期待していたのかもしれません。

しかし、あなたの説明の後に、あなたはわずかに異なるアプローチでこれらの行を追加することができます。

  1. インタフェースは、ソフトウェア開発のさまざまなチームの間で共通の理解文書として機能する規則(無視したり回避したりできないように実装する必要があるため、無視したり回避したりできないようにするための規則)です。

  2. インターフェースは、何をするべきかという考えを与えますが、それがどのようにされるのかというわけではありません。そのため、実装は指定された規則に従うことによって開発者に完全に依存します(指定されたメソッドのシグネチャを意味します)。

  3. 抽象クラスは抽象宣言、具体的な実装、またはその両方を含むことができます。

  4. 抽象宣言は従うべき規則のようなものであり、具体的な実装はガイドラインのようなものです(そのまま使用することも、独自の実装をオーバーライドして無視することによって無視することもできます)。

  5. さらに、同じシグネチャを持つどのメソッドが異なるコンテキストでの動作を変更するかもしれないかは、異なるコンテキストでそれに応じて実装するための規則としてインタフェース宣言として提供されます。

編集: Java 8では、インタフェースでデフォルトメソッドと静的メソッドを簡単に定義できます。

public interface SomeInterfaceOne {

    void usualAbstractMethod(String inputString);

    default void defaultMethod(String inputString){
        System.out.println("Inside SomeInterfaceOne defaultMethod::"+inputString);
    }
}

クラスがSomeInterfaceを実装するときに、インタフェースのデフォルトメソッドの実装を提供することは必須ではありません。

以下のメソッドを持つ別のインターフェースがあるとします。

public interface SomeInterfaceTwo {

    void usualAbstractMethod(String inputString);

    default void defaultMethod(String inputString){
        System.out.println("Inside SomeInterfaceTwo defaultMethod::"+inputString);
    }

}

Javaが複数のクラスを拡張することを許可しないのは、コンパイラがどのスーパークラスメソッドを使用するかを決定できない “ Diamond Problem” という結果になるからです。デフォルトの方法では、ダイヤモンドの問題はインタフェースにも起こります。クラスが両方を実装している場合

SomeInterfaceOne and SomeInterfaceTwo

また、共通のデフォルトメソッドを実装していないため、コンパイラはどちらを選択するかを決定できません。この問題を回避するには、Java 8では、異なるインタフェースの共通のデフォルトメソッドを実装することが必須です。いずれかのクラスが上記の両方のインタフェースを実装している場合、defaultMethod()メソッドの実装を提供する必要があります。そうしないと、コンパイラはコンパイル時エラーをスローします。

190
Shailesh Saxena

実際の使用方法と実装方法の違いについて要約しましたが、意味の違いについては何も言いませんでした。

interface は、実装クラスが持つ動作の説明です。実装クラスは、それに使用できるこれらのメソッドがあることを保証します。それは基本的にクラスがしなければならない契約または約束です。

抽象クラス は、繰り返し作成する必要のない動作を共有するさまざまなサブクラスの基礎です。サブクラスは振る舞いを完了し、事前定義された振る舞いをオーバーライドするオプションを持っていなければなりません(finalまたはprivateとして定義されていない限り)。

Listのようなインターフェースと既にインターフェースを実装しているAbstractListのような抽象クラスを含むJava.utilパッケージに良い例があります。 公式ドキュメント は、以下のようにAbstractListを記述しています。

このクラスは、「ランダムアクセス」データストア(配列など)を基盤とするこのインターフェイスの実装に必要な労力を最小限に抑えるために、Listインターフェイスのスケルトン実装を提供します。

151
Daniel Lerps

インターフェースは、シングルトン変数(public static final)とpublic abstractメソッドで構成されています。 どうすればいいのかわからないときには、リアルタイムでインターフェースを使うのが普通です。

この概念は例によってよりよく理解することができます。

支払いクラスを考えます。支払いは、Paypal、クレジットカードなど、さまざまな方法で行うことができます。したがって、通常、PaymentをmakePayment()メソッドを含むインターフェイスとして使用し、CreditCardとPaypalは2つの実装クラスです。

public interface Payment
{
    void makePayment();//by default it is a abstract method
}
public class Paypal implements Payment
{
    public void makePayment()
    {
        //some logic for Paypal payment
        //e.g. Paypal uses username and password for payment
    }
}
public class CreditCard implements Payment
{
    public void makePayment()
    {
        //some logic for CreditCard payment
        //e.g. CreditCard uses card number, date of expiry etc...
    }
}

上記の例では、CreditCardとPaypalは2つの実装クラス/戦略です。インタフェースはまた、抽象クラスでは達成できないJavaの多重継承の概念を可能にします。

何をすべきかを知っているいくつかの機能、および を実行する方法を知っているその他の機能がある場合は、抽象クラスを選択します。

次の例を見てください。

public abstract class Burger
{
    public void packing()
    {
        //some logic for packing a burger
    }
    public abstract void price(); //price is different for different categories of burgers
}
public class VegBerger extends Burger
{
    public void price()
    {
        //set price for a veg burger.
    }
}
public class NonVegBerger extends Burger
{
    public void price()
    {
        //set price for a non-veg burger.
    }
}

将来、特定の抽象クラスにメソッド(concrete/abstract)を追加する場合、実装クラスはそのコードを変更する必要はありません。しかし、将来インターフェースにメソッドを追加する場合、そのインターフェースを実装したすべてのクラスに実装を追加する必要があります。そうしないと、コンパイル時エラーが発生します。

他の違いがありますが、これらはあなたのインタビュアーが期待したものであったかもしれない主要なものです。うまくいけば、これは役に立ちました。

80
Satya

Abstactクラスとインターフェースの違い

  1. Java 8における抽象クラスとインタフェース
  2. 概念の違い

Java 8のインタフェースデフォルトメソッド

  1. デフォルト方式とは
  2. Default Methodを使用して解決されたForEachメソッドのコンパイルエラー
  3. デフォルト法と多重継承曖昧性問題
  4. Javaインタフェースのデフォルトメソッドに関する重要な点

Javaインタフェース静的メソッド

  1. Javaインタフェースの静的メソッド、コード例、静的メソッドとデフォルトのメソッド
  2. Javaインタフェースの静的メソッドに関する重要なポイント

Java機能インターフェース



Java 8における抽象クラスとインタフェース

Java 8インタフェースの変更には、インタフェースの静的メソッドとデフォルトメソッドが含まれます。 Java 8より前のバージョンでは、インターフェイスにはメソッド宣言しかありませんでした。しかし、Java 8以降、インタフェースにデフォルトのメソッドと静的メソッドを含めることができます。

Default Methodを導入した後は、インターフェイスと抽象クラスは同じように見えます。しかし、それらはJava 8ではまだ異なる概念です。

抽象クラスはコンストラクタを定義できます。それらはより構造化されており、それらに関連付けられた状態を持つことができます。対照的に、デフォルトメソッドは他のインタフェースメソッドを呼び出すという点でのみ実装でき、特定の実装の状態を参照することはできません。したがって、どちらも異なる目的で使用し、2つを選択することはシナリオのコンテキストによって異なります。

概念の違い

抽象クラスは、インタフェースの骨格(つまり部分的)実装に有効ですが、一致するインタフェースがないと存在してはいけません。

それでは、抽象クラスが見えにくくなるように効果的に縮小された場合、インタフェースの骨格実装でも、デフォルトメソッドがこれを取り除くことができるでしょうか。決定的に:いいえ!インターフェースを実装するには、ほとんどの場合、デフォルトのメソッドにはないクラス構築ツールの一部または全部が必要です。そして、何らかのインターフェースがないのであれば、それは明らかに特別なケースです。

Java 8のインタフェースデフォルトメソッド

Java 8では、 " Default Method "または(Defender methods)新機能が導入されました。これにより、開発者は既存のインターフェースの実装を中断することなく、インターフェースに新しいメソッドを追加できます。具象クラスがそのメソッドの実装を提供できない場合に、デフォルトとして使用する実装をインタフェースが定義できるようにするための柔軟性を提供します。

小さな例を考えてみましょう。

public interface OldInterface {
    public void existingMethod();
 
    default public void newDefaultMethod() {
        System.out.println("New default method"
               + " is added in interface");
    }
}

次のクラスはJava JDK 8で正常にコンパイルされます。

public class OldInterfaceImpl implements OldInterface {
    public void existingMethod() {
     // existing implementation is here…
    }
}

OldInterfaceImplのインスタンスを作成すると、

OldInterfaceImpl obj = new OldInterfaceImpl ();
// print “New default method add in interface”
obj.newDefaultMethod(); 

デフォルトの方法:

デフォルトのメソッドは最終的なものではなく、同期することもオブジェクトのメソッドをオーバーライドすることもできません。それらは常に公開されており、短くて再利用可能な方法を書く能力を厳しく制限しています。

実装にはクラスが含まれているため、実装するクラスに影響を与えることなく、デフォルトのメソッドをインタフェースに提供できます。インターフェースに追加された各メソッドがimplementationで定義されている場合、実装クラスは影響を受けません。実装クラスは、インタフェースによって提供されるデフォルト実装をオーバーライドできます。

デフォルトメソッドは、これらのインタフェースの古い実装を壊すことなく、既存のインタフェースに新しい機能を追加することを可能にします。

デフォルトのメソッドを含むインターフェースを拡張すると、次のことが可能になります。

  1. デフォルトのメソッドをオーバーライドしないで、デフォルトのメソッドを継承します。
  2. サブクラスでオーバーライドする他のメソッドと同様に、デフォルトのメソッドをオーバーライドします。
  3. デフォルトのメソッドをabstractとして再宣言します。これはサブクラスにオーバーライドさせることを強制します。

Default Methodを使用して解決されたForEachメソッドのコンパイルエラー

Java 8では、JDKコレクションが拡張され、forEachメソッドがコレクション全体に追加されました(これはラムダと連携して機能します)。従来の方法では、コードは以下のようになります。

public interface Iterable<T> {
    public void forEach(Consumer<? super T> consumer);
}

この結果、コンパイルエラーが発生したクラスをそれぞれ実装するため、既存の実装を変更しないように、必須の実装を使用してデフォルトのメソッドを追加しました。

DefaultメソッドのIterable Interfaceは以下の通りです。

public interface Iterable<T> {
    public default void forEach(Consumer
                   <? super T> consumer) {
        for (T t : this) {
            consumer.accept(t);
        }
    }
}

実装クラスを壊すことなくJDKインタフェースに Stream を追加するために同じメカニズムが使用されています。


デフォルト法と多重継承曖昧性問題

Javaクラスは複数のインタフェースを実装でき、各インタフェースは同じメソッドシグネチャでデフォルトのメソッドを定義できるため、継承されたメソッドは互いに競合する可能性があります。

以下の例を見てください。

public interface InterfaceA {  
       default void defaultMethod(){  
           System.out.println("Interface A default method");  
    }  
}
 
public interface InterfaceB {
   default void defaultMethod(){
       System.out.println("Interface B default method");
   }
}
 
public class Impl implements InterfaceA, InterfaceB  {
}

上記のコードは次のエラーでコンパイルに失敗します。

Java:クラスImplは、タイプInterfaceAおよびInterfaceBからdefaultMethod()の無関係なデフォルトを継承します。

このクラスを修正するために、デフォルトのメソッド実装を提供する必要があります。

public class Impl implements InterfaceA, InterfaceB {
    public void defaultMethod(){
    }
}

さらに、自分の実装ではなく、スーパーインタフェースのいずれかによって提供されるデフォルトの実装を呼び出したい場合は、次のようにして実行できます。

public class Impl implements InterfaceA, InterfaceB {
    public void defaultMethod(){
        // existing code here..
        InterfaceA.super.defaultMethod();
    }
}

新しい方法の一部として、デフォルトの実装またはその両方を選択できます。

Javaインタフェースのデフォルトメソッドに関する重要な点

  1. Javaインタフェースのデフォルトメソッドは、実装クラスを壊すことを恐れずにインタフェースを拡張するのに役立ちます。
  2. Javaインタフェースのデフォルトメソッドは、インタフェースと抽象クラスの違いを埋めます。
  3. Java 8インタフェースのデフォルトメソッドは、すべてのCollectionsクラスメソッドがインタフェース自体で提供されるようなユーティリティクラスを避けるのに役立ちます。
  4. Javaインタフェースのデフォルトメソッドは、基本実装クラスを削除するのに役立ちます。デフォルト実装を提供することができ、実装クラスはどちらをオーバーライドするかを選択できます。
  5. インタフェースにデフォルトメソッドを導入する主な理由の1つは、ラムダ式をサポートするためにJava 8のCollections APIを拡張することです。
  6. 階層内のいずれかのクラスに同じシグネチャを持つメソッドがある場合、デフォルトのメソッドは無関係になります。デフォルトのメソッドは、Java.lang.Objectのメソッドをオーバーライドすることはできません。その理由は非常に簡単です。これは、ObjectがすべてのJavaクラスの基本クラスであるためです。したがって、オブジェクトクラスメソッドがインタフェースのデフォルトメソッドとして定義されていても、オブジェクトクラスメソッドは常に使用されるため、無駄になります。混乱を避けるため、Objectクラスのメソッドをオーバーライドするデフォルトのメソッドを使用することはできません。
  7. Javaインタフェースのデフォルトメソッドは、DefenderメソッドまたはVirtual拡張メソッドとも呼ばれます。

リソースリンク:

  1. デフォルトメソッドとのインタフェース対Java 8の抽象クラス /
  2. JDK 8時代の抽象クラスとインタフェース
  3. 仮想拡張メソッドによるインターフェースの進化

Javaインタフェース静的メソッド

Javaインタフェース静的メソッド、コード例、静的メソッドとデフォルトのメソッド

Javaインタフェースの静的メソッドはデフォルトのメソッドと似ていますが、実装クラスでそれらをオーバーライドできない点が異なります。この機能は、実装クラスでの実装が不十分な場合に望ましくない結果を回避するのに役立ちます。簡単な例を見てみましょう。

public interface MyData {

    default void print(String str) {
        if (!isNull(str))
            System.out.println("MyData Print::" + str);
    }

    static boolean isNull(String str) {
        System.out.println("Interface Null Check");

        return str == null ? true : "".equals(str) ? true : false;
    }
}

では、実装が不十分なisNull()メソッドを持つ実装クラスを見てみましょう。

public class MyDataImpl implements MyData {

    public boolean isNull(String str) {
        System.out.println("Impl Null Check");

        return str == null ? true : false;
    }

    public static void main(String args[]){
        MyDataImpl obj = new MyDataImpl();
        obj.print("");
        obj.isNull("abc");
    }
}

IsNull(String str)は単純なクラスメソッドであり、インターフェイスメソッドをオーバーライドするものではありません。たとえば、isNull()メソッドに@Overrideアノテーションを追加すると、コンパイラエラーが発生します。

アプリケーションを実行すると、次のような出力が得られます。

インタフェースNULLチェック

暗黙のNULLチェック

インターフェースメソッドを静的からデフォルトにすると、次のような出力が得られます。

暗黙のNULLチェック

MyDataプリント::

暗黙のNULLチェック

Javaインタフェースの静的メソッドはインタフェースメソッドにのみ表示されます。MyDataImplクラスからisNull()メソッドを削除した場合、MyDataImplオブジェクトには使用できません。ただし、他の静的メソッドと同様に、クラス名を使用してインターフェースの静的メソッドを使用できます。たとえば、有効な文は次のようになります。

boolean result = MyData.isNull("abc");

Javaインタフェースの静的メソッドに関する重要なポイント

  1. Javaインタフェースの静的メソッドはインタフェースの一部です。実装クラスのオブジェクトには使用できません。
  2. Javaインタフェースの静的メソッドは、nullメソッドのチェック、コレクションのソートなどのユーティリティメソッドを提供するのに適しています。
  3. Javaインタフェースの静的メソッドは、実装クラスがそれらをオーバーライドできないようにすることでセキュリティを提供するのに役立ちます。
  4. Objectクラスのメソッドにはインターフェイスのstaticメソッドを定義することはできません。「このstaticメソッドは、インスタンスメソッドをObjectから隠すことはできません」というコンパイラエラーが発生します。これは、Objectがすべてのクラスの基本クラスであり、同じシグネチャを持つ1つのクラスレベルの静的メソッドと別のインスタンスメソッドを持つことができないため、Javaでは許可されていないためです。
  5. コレクションなどのユーティリティクラスを削除して、そのすべての静的メソッドを対応するインターフェイスに移動するには、Javaインターフェイスの静的メソッドを使用します。これは、簡単に見つけて使用できます。

Java機能インターフェース

この記事を締めくくる前に、Functionalインターフェースについて簡単に紹介したいと思います。厳密に1つの抽象メソッドを持つインターフェースは、機能インターフェースと呼ばれます。

インタフェースを機能インタフェースとしてマークするための新しいアノテーション@FunctionalInterfaceが導入されました。 @FunctionalInterfaceアノテーションは、機能的インタフェースに抽象メソッドが誤って追加されないようにするための機能です。これはオプションですが使用することをお勧めします。

関数型インタフェースは待望されており、Java 8の機能を模索しています。ラムダ式とメソッド参照のターゲット型を提供するために、多数の機能インターフェースを備えた新しいパッケージJava.util.functionが追加されています。今後の投稿では、関数型インタフェースとラムダ式について調べます。

リソースの場所:

  1. Java 8インタフェースの変更点 - staticメソッド、defaultメソッド
39
SkyWalker

最初のステートメント(Java 8リリース後)を除くすべてのステートメントは有効です:

Javaインターフェイスのメソッドは暗黙的に抽象的であり、実装することはできません

ドキュメントから ページ

インターフェイスは、クラスに似た参照型であり、定数、メソッドシグネチャ、デフォルトメソッド、静的メソッド、およびネストされた型のみを含むことができます

メソッド本体は、デフォルトメソッドと静的メソッドにのみ存在します。

デフォルトのメソッド:

インターフェイスには デフォルトメソッド を指定できますが、抽象クラスの抽象メソッドとは異なります。

デフォルトのメソッドを使用すると、ライブラリのインターフェイスに新しい機能を追加し、それらのインターフェイスの古いバージョン用に記述されたコードとのバイナリ互換性を確保できます。

デフォルトのメソッドを含むインターフェースを拡張すると、次のことができます。

  1. デフォルトのメソッドについては一切言及しないでください。これにより、拡張インターフェースはデフォルトのメソッドを継承できます。
  2. デフォルトのメソッドを再宣言して、abstractにします。
  3. デフォルトのメソッドを再定義して、それをオーバーライドします。

静的メソッド:

デフォルトのメソッドに加えて、インターフェースで静的メソッドを定義できます。 (静的メソッドは、オブジェクトではなく、定義されているクラスに関連付けられているメソッドです。クラスのすべてのインスタンスは、静的メソッドを共有しています。)

これにより、ライブラリでヘルパーメソッドを簡単に整理できます。

interfaceおよびstaticメソッドを持つdefaultに関するドキュメントページのサンプルコード。

import Java.time.*;

public interface TimeClient {
    void setTime(int hour, int minute, int second);
    void setDate(int day, int month, int year);
    void setDateAndTime(int day, int month, int year,
                               int hour, int minute, int second);
    LocalDateTime getLocalDateTime();

    static ZoneId getZoneId (String zoneString) {
        try {
            return ZoneId.of(zoneString);
        } catch (DateTimeException e) {
            System.err.println("Invalid time zone: " + zoneString +
                "; using default time zone instead.");
            return ZoneId.systemDefault();
        }
    }

    default ZonedDateTime getZonedDateTime(String zoneString) {
        return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }
}

以下のガイドラインを使用して、インターフェースを使用するか抽象クラスを使用するかを選択します。

インターフェース:

  1. contractを定義するには(できればステートレス-変数がないことを意味する)
  2. 無関係なクラスをとリンクするには、機能があります。
  3. パブリック定数変数を宣言するには(immutable state

抽象クラス:

  1. 密接に関連するいくつかのクラス間でコードを共有します。それは関係であることを確立します。

  2. 関連クラス間で共通の状態を共有する(具象クラスで状態を変更できます)

関連記事:

インターフェイスと抽象クラス(一般的なオブジェクト指向)

実装と拡張:どのような場合に使用しますか?違いは何ですか?

これらの例を確認することで、次のことを理解できます

無関係なクラスはインターフェースを介して機能を持つことができますが、関連するクラスは基本クラスの拡張により動作を変更します。

39
Ravindra babu

あなたの説明はまともに見えますが、教科書からすべて読んでいたように見えるかもしれませんか。 : - /

私がもっと悩んでいるのは、あなたの例がどれほど堅実だったかということです。 ほぼ 抽象とインタフェースの間のすべての違いを含めても構わないのですか?

個人的には、私はこのリンクをお勧めします: http://mindprod.com/jgloss/interfacevsabstract.html#TABLE

違いの網羅的なリストについては..

今後のインタビューであなたと他のすべての読者の役に立つことを願います。

21
VictorCreator

多くのジュニア開発者は、インターフェイス、抽象クラスおよび具象クラスを同じことのわずかなバリエーションと考える間違いを犯し、純粋に技術的な理由でそれらのいずれかを選択します:多重継承が必要ですか?一般的なメソッドを配置する場所は必要ですか?具体的なクラス以外のものに悩む必要がありますか?これは間違っており、これらの質問に隠されていることが主な問題です"I"。自分でコードを書くとき、他の現在または将来の開発者が自分のコードに取り組んでいる、またはコードで作業していることを考えることはめったにありません。

インターフェイスと抽象クラスは、技術的な観点からは明らかに似ていますが、まったく異なる意味と目的を持っています。

概要

  1. インターフェース契約を定義するいくつかの実装があなたのために満たすこと。

  2. 抽象クラスデフォルトの動作を提供そのあなたの実装は再利用できます。

上記の2つのポイントは、インタビューの際に私が探しているものであり、十分にコンパクトな要約です。詳細をお読みください。

代替の要約

  1. インターフェイスは、パブリックAPIを定義するためのものです
  2. 抽象クラスは内部使用のため、およびSPIを定義するためのものです

例によって

別の言い方をすると、具体的なクラスが実際の作業を非常に具体的な方法で行います。たとえば、ArrayListは、メモリの連続領域を使用して、オブジェクトのリストをコンパクトな方法で格納します。これにより、高速なランダムアクセス、反復、インプレース変更が可能になりますが、挿入、削除、場合によっては追加でさえひどくなります。一方、LinkedListは二重リンクノードを使用してオブジェクトのリストを格納します。これにより、代わりに高速の反復、インプレース変更、挿入/削除/追加が提供されますが、ランダムアクセスではひどいです。これら2つのタイプのリストは、異なるユースケースに合わせて最適化されており、どのように使用するかが非常に重要です。頻繁にやり取りしているリストからパフォーマンスを絞り出そうとする場合、およびリストの種類を選択するのはあなた次第であるため、インスタンス化するリストを慎重に選択する必要があります。

一方、リストの高レベルのユーザーは、実際にリストがどのように実装されるかを気にしません。これらの詳細から隔離する必要があります。 JavaがListインターフェースを公開しなかったが、実際にはListが実際に何であるかという具体的なLinkedListクラスのみがあったと想像してみましょう。すべてのJava開発者は、実装の詳細に合わせてコードを調整します:ランダムアクセスを回避する、キャッシュを追加してアクセスを高速化する、またはArrayListを独自に再実装しますが、他のすべてのコードとは互換性がありません実際にはListのみで機能します。それはひどいことでしょう...しかし、Javaマスターが実際にほとんどの実際のユースケースでリンクリストがひどいことを認識し、利用可能なListクラスのみの配列リストに切り替えることにしたと想像してください。これは、世界中のすべてのJavaプログラムのパフォーマンスに影響し、人々はそれについて満足しません。そして、主な犯人は、実装の詳細が利用可能であったことであり、開発者は、それらの詳細は信頼できる永続的な契約であると想定していました。そのため、実装の詳細を非表示にし、抽象コントラクトのみを定義することが重要です。これがインターフェイスの目的です。プログラマがコードを微調整して、将来の更新で変更される可能性のある内部の詳細に合わせることを誘惑するすべての内臓を公開することなく、メソッドが受け入れる入力の種類と出力が期待される種類を定義します。

抽象クラスは、インターフェイスと具象クラスの中間にあります。実装が一般的なコードまたは退屈なコードを共有するのに役立つはずです。たとえば、AbstractCollectionは、サイズが0に基づくisEmptyの基本的な実装、反復および比較としてのcontains、繰り返されるaddAllとしてのaddなどを提供します。これにより、実装はそれらを区別する重要な部分、つまり実際にデータを保存および取得する方法に集中できます。

別の視点:APIとSPI

インターフェースは、コードのさまざまな部分の間の結束力が低いゲートウェイです。これにより、内部で何かが変更されたときにすべてのライブラリユーザーに影響を与えることなく、ライブラリの存在と進化を可能にします。これはApplication Programming Interfaceと呼ばれ、Application Programming Classesではありません。小規模では、複数の開発者が、十分に文書化されたインターフェースを介して異なるモジュールを分離することにより、大規模プロジェクトで正常に共同作業することもできます。

抽象クラスは結束性が高いhelpers実装の詳細のある程度のレベルを想定して、インターフェースを実装するときに使用されます。あるいは、抽象クラスはSPI、サービスプロバイダーインターフェイスの定義に使用されます。

APIとSPIの違いは微妙ですが、重要です。APIの場合、焦点はses itであり、SPIの場合は焦点は、誰に実装 itです。

APIへのメソッドの追加は簡単で、APIの既存のユーザーはすべてコンパイルされます。すべてのサービスプロバイダー(具体的な実装)が新しいメソッドを実装する必要があるため、SPIにメソッドを追加するのは困難です。インターフェイスを使用してSPIを定義する場合、SPIコントラクトが変更されるたびに、プロバイダーは新しいバージョンをリリースする必要があります。代わりに抽象クラスが使用される場合、新しいメソッドは既存の抽象メソッドの観点から、または少なくとも古いバージョンのサービス実装をコンパイルおよび実行できる空のthrow not implemented exceptionスタブとして定義できます。

Java 8およびデフォルトのメソッドに関する注意

Java 8はインターフェースのデフォルトのメソッドを導入しましたが、これによりインターフェースと抽象クラスの間の線がさらにぼやけますが、これは実装がコードを再利用できるようにするためではなく、 APIおよびSPIとして(または抽象クラスの代わりにSPIの定義に誤って使用されます)。

「本の知識」

OPの回答で提供される技術的な詳細は、「書籍の知識」と見なされます。これは、これが通常、学校や言語に関するほとんどの技術書籍で使用されるアプローチであるためです。what a is is、nothow実際に、特に大規模なアプリケーションで使用する場合。

アナロジーは次のとおりです。質問は次のとおりです。

プロムナイト、車、またはホテルの部屋を借りる方が良いでしょうか?

技術的な答えは次のように聞こえます:

まあ、車ではもっと早くできますが、ホテルの部屋ではもっと快適にできます。一方、ホテルの部屋は1か所にしかありませんが、車内では、Vistaポイントに行って素敵な景色を眺めたり、ドライブインシアターに行ったり、または他の多くの場所、さらには複数の場所。また、ホテルの部屋にはシャワーがあります。

それはすべて真実ですが、2つの完全に異なるものであり、両方を同時に異なる目的に使用できるという点を完全に見失い、「行う」という側面は2つのオプションのいずれかについて最も重要なことではありません。答えには遠近感がなく、真の「事実」を正しく提示しながら、未熟な考え方を示しています。

20
Sergiu Dumitriu

インタフェースは、コントラクトを実装するクラスがメソッドの実装を約束する「コントラクト」です。クラスではなくインターフェースを作成しなければならなかった例は、ゲームを2Dから3Dにアップグレードしたときでした。私はゲームの2D版と3D版の間でクラスを共有するためのインターフェースを作成しなければなりませんでした。

package adventure;
import Java.awt.*;
public interface Playable {
    public void playSound(String s);
    public Image loadPicture(String s);    
}

それから、どのバージョンのゲームをロードしているのかわからないオブジェクトからそれらのメソッドを呼び出すことができながら、環境に基づいてメソッドを実装できます。

public class Adventure extends JFrame implements Playable

public class Dungeon3D extends SimpleApplication implements Playable

public class Main extends SimpleApplication implements AnimEventListener, ActionListener, Playable

通常、ゲームの世界では、世界はゲームのメソッドを実行する抽象クラスになります。

public abstract class World...

    public Playable owner;

    public Playable getOwner() {
        return owner;
    }

    public void setOwner(Playable owner) {
        this.owner = owner;
    }
9

次のように考えてはどうでしょうか。

  • クラスと抽象クラスの間の関係は "is-a"型です。
  • クラスとインタフェース間の関係は "has-a"型です。

したがって、Mammalsという抽象クラス、Humanというサブクラス、およびDrivingというインタフェースがある場合は、次のように表現できます。

  • 各人間は哺乳類です
  • それぞれの人間は運転する(行動)

私の提案は、本のナレッジフレーズは、彼が両者の意味の違いを聞きたがっていたことを示しています(他の人がすでに示唆しているように)。

8
akohout

抽象クラスは純粋な抽象概念ではありません。具体的な(実装されたメソッドの)コレクションや未実装のメソッドの集まりです。しかし、インターフェースは純粋な抽象化であり、未実装のメソッドのみがあり、具体的なメソッドはありません。

なぜ抽象クラスなのか

  1. ユーザーがすべてのオブジェクトに共通の機能を書きたい場合。
  2. 抽象クラスは、エンドユーザーに影響を与えずに機能を追加するための将来の再実装に最適です。

なぜインターフェイス?

  1. ユーザーがオブジェクト上の異なる機能となる異なる機能を書きたい場合。
  2. インタフェースは、インタフェースが公開された後に要件を変更する必要がない場合に最適な選択肢です。
6
Sarfaraz Ahamad

interface はある種の効果を持つことが公的に文書化されている遺伝子のセットのようなものです:DNAテストは私がそれらを持っているかどうかを教えてくれます - 私は "キャリア"であり、私の行動や状態の一部はそれらに準拠します。 (もちろん、私はこの範囲外の形質を提供する他の多くの遺伝子を持っているかもしれません。)

抽象クラス は、 シングルセックス種の死んだ祖先のようなもの (*):彼女は命を吹き込むことはできないが生きている(すなわち非抽象子孫は彼女のすべての遺伝子を受け継ぐ。

(*)この比喩を広げるために、種のすべてのメンバーが同じ年齢に住んでいるとしましょう。これは、死んだ先祖のすべての先祖もまた死んでいなければならないことを意味します - 同様に、生きている先祖のすべての子孫も生きていなければなりません。

5
Steve Chambers

私は仕事のために面接をします、そして、私はあなたの答えにも同様に不利に見えるでしょう(申し訳ありませんが、非常に正直です)。その違いについて読んで答えを修正したように思えますが、実際にそれを使ったことは一度もありません。

あなたがそれぞれを使う理由についての良い説明は、違いの正確な説明をするよりはるかに良いことがあります。雇用主は最終的にプログラマーにインタビューで証明することが困難である場合もあるそれらを知らない事をしてほしいと思う。あなたが与えた答えは、技術的または文書ベースの仕事に応募するが開発者の役割に応募しない場合には良いでしょう。

今後のインタビューで頑張ってください。

また、この質問に対する私の答えは、あなたが提供した技術資料ではなく、インタビューテクニックに関するものです。おそらくそれについて読むことを検討してください。 https://workplace.stackexchange.com/ は、このようなことに最適な場所です。

4

インターフェースは純粋に抽象的です。インターフェースに実装コードはありません。

抽象クラスはメソッドとその実装の両方を含みます。

インタフェースと抽象クラスのチュートリアルを見るにはここをクリック

3
user2822053

私が観察した主な違いは、抽象クラスはすでに実装されているいくつかの一般的な動作を提供し、サブクラスはそれらに対応する特定の機能を実装するだけでよいことです。ここで、インターフェースに関してはどのタスクを実行する必要があるのか​​を指定するだけで、インターフェースによる実装はありません。それはそれ自身と実装されたクラスの間の契約を指定すると言えるでしょう。

3
user2729112

私も同じ質問に複数のインタビューで直面していて、インタビュアーを納得させるのはあなたの時間を悲惨なものにすると私は信じています。上記のすべての答えを内在するのであれば、OOを最大限に活用するために、もう1つ重要な点を追加する必要があります。

そうでない場合 ルールの変更を計画する サブクラスに従うためには、長い間、変更することができないのでインターフェースのために行ってください。そうすると、他のすべてのサブクラスの変更のために移動する必要があります。あなたは考える、 あなたは機能を再利用し、いくつかのルールを設定し、またそれを変更のためにオープンにしたいです。、抽象クラスに行きます。

このように考えてみてください、あなたは消耗品サービスを使用したか、あなたが世界に何らかのコードを提供した、そしてあなたは何かを修正する機会があります。私のEclipseですべての既読マークを見つけると、アプリケーション全体が停止します。そのため、そのような悪夢を防ぐために、Abstract over Interfacesを使用してください。

私はこれがインタビュアーをある程度納得させるかもしれないと思います。

3
vinod bazari

多重継承でのDiamondの問題を回避するには、JavaでInterfaceを選択してください

あなたのすべてのメソッドがあなたのクライアントによって実装されることを望むなら、あなたはインターフェースに行きます。つまり、アプリケーション全体を抽象的に設計するということです。

何が共通しているのかをすでに知っていれば、抽象クラスを選択します。例えば、抽象クラスCarを取ります。より高いレベルでは、calculateRPM()のような一般的な自動車メソッドを実装します。これは一般的な方法であり、クライアントに次のような独自の動作を実装させることができます。
calculateMaxSpeed()などおそらく、あなたは毎日の仕事で遭遇したいくつかの実例を挙げて説明したでしょう。

3
MaheshVarma

2つの密接に関連したクラス間で動作を共有しようとしているときは、共通の動作を保持し、両方のクラスの親として機能する抽象クラスを作成します。

Typeを定義しようとしているとき、私のオブジェクトのユーザーが確実に呼び出すことができるメソッドのリスト、それから私はインターフェースを作成します。

たとえば、抽象クラスは振る舞いを共有するためのものであるため、1つの具象サブクラスを持つ抽象クラスを作成することは決してありません。しかし、私は1つの実装だけを持つインターフェースを作成することができます。私のコードのユーザーは、実装が1つしかないことを知りません。確かに、将来のリリースではいくつかの実装があるかもしれません、それらのすべては私がインターフェースを作成したときにさえ存在しなかったいくつかの新しい抽象クラスのサブクラスです。

それはあまりにもあまりにも本を読み過ぎたように思えたかもしれません(私が思い出すことができるようにそれがそのように置かれるのを見たことはありませんでしたが)。インタビュアー(またはOP)がそれについてもっと私の個人的な経験を本当に望んでいるならば、私はインターフェースの逸話で準備ができていたでしょう、そしてその逆もまた同様です。

もう一つ。 Java 8では、デフォルトコードをインタフェースに追加できるようになりました。これにより、インタフェースと抽象クラスの境界がさらにぼやけます。しかし、私がこれまで見てきたことから、その機能はJavaコアライブラリのメーカーによってさえも過剰に使用されています。この機能は、バイナリ互換性を損なうことなくインタフェースを拡張できるようにするために追加されたものです。しかし、インターフェースを定義してまったく新しいTypeを作成するのであれば、そのインターフェースはちょうどインターフェースになるはずです。共通のコードも提供したい場合は、必ず補助クラス(抽象クラ​​スまたは具象クラス)を作成してください。あなたが変更したいと思うかもしれない機能性であなたのインターフェースを始めから混乱させないでください。

2
Teto

この2つの違いを示すために、実用的なシナリオを使用して答えようとします。

インタフェースは、ペイロードがゼロである、すなわち状態を維持する必要がないため、単にコントラクト(機能)をクラスに関連付けるためのより良い選択です。

たとえば、何らかのアクションを実行するTaskクラスがあり、別のスレッドでタスクを実行するためにThreadクラスを拡張する必要がないとします。より良い選択は、TaskにRunnableインターフェースを実装することです(つまり、run()メソッドを実装します)。次に、このTaskクラスのオブジェクトをThreadインスタンスに渡して、そのstart()メソッドを呼び出します。

今度はRunnableが抽象クラスだった場合どうするか尋ねることができますか。

技術的にはそれは可能でしたが、設計上の賢明さは悪い選択であったでしょう。

  • Runnableには状態が関連付けられておらず、run()メソッドのデフォルト実装も「提供」されていません。
  • タスクはそれを拡張する必要があるので他のクラスを拡張することはできませんでした
  • タスクはRunnableクラスへの特殊化として提供するものは何もありません、それが必要とするすべてはrun()メソッドをオーバーライドすることです

言い換えれば、Taskクラスは、それをスレッドにするThreadクラスを拡張したRunnableインターフェース対を実装することによって達成されるスレッドで実行される機能を必要としていました。

抽象クラスを使用してその機能のスケルトン(共通/部分)実装を定義する一方で、単に機能(コントラクト)を定義するために私たちのインターフェースを置くだけです。

免責事項: /愚かな例が続きます、判断しないでください:-P

interface Forgiver {
    void forgive();
}

abstract class GodLike implements Forgiver {
    abstract void forget();
    final void forgive() {
        forget();
    }
}

今、あなたはGodLikeであることの選択を与えられました、しかしあなたは寛容者だけであることを選ぶかもしれません(すなわち、GodLikeではありません)そして、すること:

class HumanLike implements Forgiver {
    void forgive() {
       // forgive but remember    
    }
}

あるいは、あなたはGodLikeであることを選ぶかもしれません:

class AngelLike extends GodLike {
    void forget() {
       // forget to forgive     
    }
}

P.S Java 8インターフェースを使用すると、静的メソッドとデフォルト(オーバーライド可能な実装)メソッドを持つこともできるため、モノクロインターフェースと抽象クラスの違いがさらに絞り込まれます。

1
sactiw

はい、あなたの回答は技術的には正しいのですが、あなたが間違ったところにそれらを見せることではなく、一方を選択することの長所と短所を理解していました。さらに、彼らはおそらく将来のアップグレードと彼らのコードベースの互換性について懸念していました。このタイプの応答は、(あなたが言ったことに加えて)助けになったかもしれません:

「インタフェースクラスよりも抽象クラスの選択は、コードの将来がどのようになるかによって異なります。

既存のコードを壊すことなく抽象クラスに今後も振る舞いを追加し続けることができるので、抽象クラスはより優れた前方互換性を可能にします - >これはインタフェースクラスでは不可能です。

一方、インタフェースクラスは抽象クラスよりも柔軟性があります。これは 複数のインターフェースを実装できるからです 。つまり、Javaには多重継承がないため、抽象クラスを使用しても他のクラス階層構造を使用することはできません。

したがって、結局のところ、一般的な経験則は以下のとおりです。コードベースに既存の実装やデフォルトの実装がない場合は、インターフェイスクラスを使用することをお勧めします。将来クラスを更新する予定がある場合は、抽象クラスを使用して互換性を維持してください。」

次のインタビューで頑張ってください!

1
Jaxian

うーん今人々は卑劣な実用的なアプローチです、あなたは非常に正しいですが、インタビュアーのほとんどは彼らの現在のrequirmentに従って見え、実用的なアプローチを望んでいます。

あなたの答えを終えた後、あなたは例に飛ぶべきです:

要約:

たとえば、給与関数にはすべての従業員に共通のパラメータがあります。それから部分的に定義されたメソッド本体を持つCTCと呼ばれる抽象クラスを持つことができます、そしてそれは従業員のすべてのタイプによって拡張されて、彼らの余分な牛肉によって再定義されるでしょう。一般的な機能性のために。

public abstract class CTC {

    public int salary(int hra, int da, int extra)
    {
        int total;
        total = hra+da+extra;
        //incentive for specific performing employee
        //total = hra+da+extra+incentive;
        return total;
    }
}

class Manger extends CTC
{
}


class CEO extends CTC
{
}

class Developer extends CTC
{   
}

インターフェース

javaのインターフェースはそれを拡張することなくinterfcae機能を持つことを可能にします、そしてあなたはあなたがあなたのアプリケーションで導入したい機能のシグネチャの実装で明確でなければなりません。それはあなたが定義を持つことを強制するでしょう。さまざまな機能のために。

public interface EmployeType {

    public String typeOfEmployee();
}

class ContarctOne implements EmployeType
{

    @Override
    public String typeOfEmployee() {
        return "contract";
    }

}

class PermanentOne implements EmployeType
{

    @Override
    public String typeOfEmployee() {
        return "permanent";
    }

}

抽象クラスとして定義された方法によっても抽象クラスでそのような強制的なアクティビティを持つことができます。今や抽象クラスを継承するクラスは抽象クラスをオーバーライドするまで抽象クラスを継承します。

1
RTA

abstractname__クラスの実用的な実装についてもう1つのポイントを追加します。

abstractname__キーワードも、クラスがインスタンス化されないようにするためだけに使用されます。インスタンス化したくない具象クラスがある場合 - abstractname__にします。

1
Blade

私が理解したことと私がどのようにアプローチするかから、

インタフェースは仕様/規約のようなもので、インタフェースクラスを実装するクラスはすべて抽象クラスで定義されたすべてのメソッドを実装する必要があります(デフォルトのメソッド(Java 8で導入されたメソッドを除く))。

クラスのいくつかのメソッドといくつかのメソッドに必要な実装がわかっているときはクラス抽象を定義しますが、実装がどうなるかわかりません(関数のシグネチャはわかっているが実装はわからない)。私はこれらのメソッドがどのように実装されるべきかを知ったときに後の開発の部分で私がちょうどこの抽象クラスを拡張してこれらのメソッドを実装することができるようにこれをする。

注:メソッドが静的またはデフォルトでない限り、インターフェイスメソッドに関数本体を含めることはできません。

1
Akash Lodha

抽象クラスでは、メソッドのデフォルト実装を書くことができます。しかし、インターフェースではできません。基本的に、インターフェースにはインターフェースを実装するクラスによって実装されなければならない純粋な仮想メソッドがあります。

1
dg_no_9

インタフェースと抽象クラスの基本的な違いは、インタフェースは多重継承をサポートしますが抽象クラスはサポートしないということです。

抽象クラスでもインターフェースのようなすべての抽象メソッドを提供できます。

なぜ抽象クラスが必要なのか

いくつかのシナリオでは、ユーザの要求を処理している間、抽象クラスはユーザの意図を知らない。そのシナリオでは、クラス内で1つの抽象メソッドを定義し、このクラスを拡張したユーザーに抽象メソッドでの意図を提供してください。この場合、抽象クラスは非常に便利です。

なぜインターフェースが必要なのですか?

たとえば、私はその分野での経験がない作品があります。たとえば、建物やダムを建設したい場合、そのシナリオで何をするでしょうか。

  1. あなたはあなたの要求が何であるかを識別しそしてその要求と契約をするでしょう。
  2. それからあなたのプロジェクトを組み立てるために入札を呼んでください
  3. 誰がプロジェクトを構築するか、あなたの要求を満たすべきです。しかし、構築ロジックはベンダーごとに異なります。

ここでは、それらがどのように構築されたかについての論理については気にしません。最終的なオブジェクトは私の要求を満たしているかどうかではなく、それが私の要点だけでした。

ここであなたの要求はインターフェースと呼ばれ、コンストラクタは実装者と呼ばれます。

1
Dharisi Suresh

私が理解していることから、実装されていない最終的な変数とメソッドで構成されているインタフェースは、クラスによって実装され、互いに関連のある一連のメソッドまたはメソッドを取得します。一方、抽象クラスは、実装を伴う最終的ではない変数やメソッドを含むことができますが、通常はガイドとして、またはすべての関連クラスまたは類似クラスが継承するスーパークラスとして使用されます。言い換えれば、抽象クラスは、そのすべてのサブクラスによって共有されるすべてのメソッド/変数を含みます。

1
Marz

インタビュアーがやろうとしていたのは、おそらくインターフェースと実装の違いです。

Javaモジュールではなく、より一般的に言うと "コード"モジュールへの "インターフェイス"は、基本的に、インターフェイスを使用するクライアントコードとの契約です。

コードモジュールの実装は、モジュールを機能させるための内部コードです。多くの場合、特定のインターフェイスを複数の異なる方法で実装でき、さらにクライアントコードがその変更を認識していなくても実装を変更できます。

実装を指定せずに、クラスを使用するクライアントコードの利点のためにクラスがどのように動作するかを定義するために、Javaインタフェースは上記の一般的な意味でのインタフェースとしてのみ使用されるべきです。したがって、インタフェースには、クライアントコードから呼び出されることが予想されるメソッドのメソッドシグネチャ(名前、戻り値の型、および引数リスト)が含まれています。原則として、メソッドごとに、そのメソッドの動作を説明するJavadocが多数あります。インターフェースを使用する最も説得力のある理由は、インターフェースの実装を複数にする予定の場合、おそらくデプロイメント構成に応じて実装を選択することです。

対照的に、Java抽象クラスは、インタフェースを指定するという主な目的を持つのではなく、クラスの部分的な実装を提供します。複数のクラスがコードを共有するとき、しかしサブクラスが実装の一部を提供することも予想されるときに使用されるべきです。これにより、共有コードを1つの場所(抽象クラ​​ス)にのみ表示できますが、実装の一部は抽象クラスには存在せず、サブクラスによって提供されることが予想されます。

0
Warren Dew

あなたの答えは正しいのですが、インタビュアーはJavaの詳細ではなくソフトウェア工学の観点から区別する必要があります。

簡単な言葉:

インタフェースは、店舗のインタフェースに似ています。表示されているものはすべて店舗内に存在するはずなので、インタフェース内のメソッドはすべて具象クラスに実装されている必要があります。いくつかのクラスがいくつかの正確なメソッドを共有し、他のものと異なる場合はどうなりますか。 Interfaceが2つのものを含むショップに関するもので、2つのショップに両方ともスポーツ用品が入っているが、一方には余分な服があり、もう一方には余分な靴があるとします。それであなたがすることは、Sportsメソッドを実装し、他のメソッドを未実装のままにするSportの抽象クラスを作ることです。ここで抽象クラスとは、このショップ自体は存在しないことを意味しますが、それは他のクラス/ショップの基盤です。このようにして、コードを体系化し、コードを複製し、コードを統合し、他のクラスによる再利用性を確保するというエラーを回避します。

0
Mosab Shaheen