正しいメソッドを持っているよりもインターフェイスにもっとありますか


159

だから私がこのインターフェースを持っているとしましょう:

public interface IBox
{
   public void setSize(int size);
   public int getSize();
   public int getArea();
  //...and so on
}

そして私はそれを実装するクラスを持っています:

public class Rectangle implements IBox
{
   private int size;
   //Methods here
}

IBoxインターフェイスを使用したい場合、実際にはそのインスタンスを作成できません。

public static void main(String args[])
{
    Ibox myBox=new Ibox();
}

正しい?だから私は実際にこれをしなければならないでしょう:

public static void main(String args[])
{
    Rectangle myBox=new Rectangle();
}

それが本当である場合、インターフェイスの唯一の目的は、インターフェイスを実装するクラスが、そのインターフェイスに記述されている正しいメソッドを持っていることを確認することですか?または、インターフェースの他の用途はありますか?


2
覚えておいてください、インターフェースはJavaに固有のものではありません。すべてのOOP言語は、何らかの形でそれらを持っていますが、常にJavaとして明示的に定義されているわけではありません。
Herms

2
技術的には、すべての強く型付けされたOOP言語は何らかの形でそれらを持っています。型なし、またはダック型の言語には、同様の概念はありません。
ジャレド

1
@Jared強い型付けと静的型付け、および「型なし」と動的型付けを混同していませんか?
eljenso

ポリモーフィズムは、インターフェースを介して実現することもできます。このページの最後のセクションを確認してくださいcodenuggets.com/2014/06/20/java-interface
Jeff

回答:


143

インターフェイスは、コードをより柔軟にする方法です。あなたがすることはこれです:

Ibox myBox=new Rectangle();

その後、別の種類のボックスを使用する場合(おそらく、より適切な種類のボックスを持つ別のライブラリがある)、コードを次のように切り替えます。

Ibox myBox=new OtherKindOfBox();

慣れてしまえば、それは素晴らしい(実際には不可欠な)作業方法であることがわかります。

もう1つの理由は、たとえば、ボックスのリストを作成して、それぞれにいくつかの操作を実行したいが、リストにさまざまな種類のボックスを含めたい場合です。各ボックスで次のことができます:

myBox.close()

(IBoxがclose()メソッドを持っていると仮定)myBoxの実際のクラスは、反復でどのボックスにいるかによって変化します。


54
この回答には、Javaインターフェースに限定されるものはありません。同じことが抽象クラスにも、具体的なクラスにも等しく当てはまります。複数のインターフェースを実装する機能、およびそれがいつ、どのような場合に役立つのかについて言及するのに良い答えを期待しています。
ホジェリオ

16
これはどのようにして答えとして選ばれたのですか?これは、ポリモーフィズムが役立つ理由の簡単な説明ですが、上記のポスターが言ったように、複数のインターフェイスのより良い説明を期待し、さらに重要なのは、インターフェイスと抽象クラスの使用が適切な場合です。
trevorkavanaugh 2014年

2
これは、インターフェイスの説明や、ポリモーフィズムの基本に関するすべてのこととはほとんど関係が
ありません

123

インターフェースが便利になるのは、「後で気が変わって別の実装を使用でき、オブジェクトが作成された1つの場所を変更するだけでよい」という事実ではありません。それは問題ではありません。

本当の意味はすでに名前にあります:それらは、そのインターフェースで動作するすべてのコードを使用するために誰でも実装できるインターフェースを定義します。最良の例はjava.util.Collectionssort()またはなどのインターフェイスで排他的に動作するあらゆる種類の便利なメソッドを提供するものreverse()ですList。ここでのポイントは、このコードは、現在のソートに使用されるか、または逆にすることができるということである任意の実装するクラスをListだけではなく-インタフェースArrayListLinkedList書いた人々は方法で実装することができるあなた自身を記述することなく、クラス、java.util.Collections想像もしませんでした。

同様に、既知のインターフェース、または定義したインターフェースで動作するコードを記述できます。他の人は、クラスのサポートを依頼することなくコードを使用できます。

インターフェースのもう1つの一般的な用途は、コールバックです。たとえば、java.swing.table.TableCellRendererを使用すると、Swingテーブルが特定の列のデータを表示する方法に影響を与えることができます。そのインターフェースを実装し、インスタンスをに渡しJTable、テーブルのレンダリング中のある時点で、コードはその処理を行うために呼び出されます。


9
これはかなり良い答えです
。Java

1
気に入ったyou can write code that operates on well-known interfaces, or interfaces you define
マニッシュクマー14

ちょっと待ってください...インターフェイスが便利になるのは、[好きな実装を使用できる]ではなく、[好きな実装を使用できる]ですか。ただし、反対のケースについて言及することは、かなり良い点です。
Powerslave 2014年

4
@Powerslave:言い換えると、インターフェースを便利にするのは、[実装を変更するときに1行だけ変更する必要があるコードを作成する機能]ではなく、[実装を指定しないコードを作成する機能]です。すべて]。
Michael Borgwardt 2014年

@MichaelBorgwardtいいですね。:)明確にしてくれてありがとう!
Powerslave 2014年

119

私が読んだ多くの用途の1つは、Javaで複数の継承を使用するインターフェースなしでは難しいところです。

class Animal
{
void walk() { } 
....
.... //other methods and finally
void chew() { } //concentrate on this
} 

ここで、次のようなケースを想像してみてください。

class Reptile extends Animal 
{ 
//reptile specific code here
} //not a problem here

だが、

class Bird extends Animal
{
...... //other Bird specific code
} //now Birds cannot chew so this would a problem in the sense Bird classes can also call chew() method which is unwanted

より良いデザインは:

class Animal
{
void walk() { } 
....
.... //other methods 
} 

動物にはchew()メソッドがなく、代わりに次のようにインターフェイスに配置されます。

interface Chewable {
void chew();
}

そして爬虫類のクラスにこれを実装し、Birdsは実装しないでください(Birdsはかむことができないため)。

class Reptile extends Animal implements Chewable { } 

鳥の場合は単純に:

class Bird extends Animal { }

6
@CHEBURASHKAそして悪いネーミング。Reptile「噛む」場合、それ自体は「噛む」ことができません。大会(時々 )のインターフェースを命名Whateverableそれは完璧な理にかなっているところのみ適用されるべきです。Predatorここでは、インターフェースに名前を付ける方が適切です。
Powerslave 2014年

6
@Powerslave正解です。爬虫類は「噛むことができます」/「噛める」です。鷹は捕食者ですが、それでも噛むことはできません...ただつまんでいるだけですが、「チュアブル」はインターフェースのドキュメントでより適切に定義できます。
Madmenyo 2014年

素晴らしい..非常によく説明しました。ありがとうございました..!
グルシンゲ2015

1
わかりません。インターフェースは、それを実装する各クラスに同じメソッドを実装することを確実にするための良い方法であるように思われます(たとえば、run()とDogクラスでBirdクラスの愚かな選択ができないようにします) runn()を使用すると、それらはすべて同じです)。しかし、注意を払い、クラスに同じメソッド形式/構造を持たせることで、これと同じことを実現できませんでしたか?本当にインターフェースがプログラマのことを忘れないようにしているかのようです。また、インターフェースを使用しても時間を節約できないようです。それを実装する各クラスでメソッドを定義する必要があります。
Alex G

@AlexG-私は多くの用途の1つを言った:)もっとあります、私たちはかろうじて表面を引っ掻いて単純な方法で質問に答えました!
ピーシー2016年

47

インターフェイスの目的は、ポリモーフィズム、つまり型の置換です。たとえば、次のメソッドがあるとします。

public void scale(IBox b, int i) {
   b.setSize(b.getSize() * i);
}

scaleメソッドを呼び出すときに、IBoxインターフェースを実装するタイプの値を提供できます。言い換えれば、場合RectangleSquare実装の両方IBox、あなたはどちらか提供することができますRectangleSquareどこIBox期待されています。


8
サブクラス化とメソッドのオーバーライドを使用してJavaでそれをすでに達成できる場合、なぜインターフェースの多態性の目的があるのですか?
eljenso

1
インターフェースが実装を省略しなければならないことを除いて、それは同じことです。したがって、クラスは複数のインターフェースを実装できます。
Apocalisp 2009

4
ねえ、Javaになんらかの概念的な整合性があるとは言ったことがありません。タイプの置換は、すべてのサブタイピングの目的です。Javaには、複数のサブタイピングメカニズムがありますが、どれも特に優れているわけではありません。
Apocalisp 2009

1
概念の整合性についても何も言いませんでした。しかし、先に進みましょう。メソッドですべてのIBoxをスケーリングできる場合、それはIBoxで宣言された操作ではありませんか?IBox.scale(int)?
eljenso

1
IntegerをIBoxに結合したくないので、Integerのメソッドにはしません。また、インターフェイス上のメソッドの数は、それを実装するのがいかに面倒かではなく、それが表現する抽象化の一貫性とまとまりによって決まります。とにかく、あなたの答えアポをありがとう。
eljenso 2009

33

インターフェイスを使用すると、静的に型付けされた言語でポリモーフィズムをサポートできます。オブジェクト指向の純粋主義者は、言語が完全な機能を備えたオブジェクト指向言語であるためには、継承、カプセル化、モジュール性、多態性を提供するべきだと主張するでしょう。動的型付け-またはアヒル型付け-言語(Smalltalkなど)では、ポリモーフィズムは簡単です。ただし、静的に型付けされた言語(JavaやC#など)では、ポリモーフィズムは自明ではありません(実際、表面的には、強い型付けの概念とは矛盾しているようです)。

実演させてください:

動的型付け(またはアヒル型付け)言語(Smalltalkなど)では、すべての変数はオブジェクトへの参照です(それ以上でもそれ以下でもありません)。したがって、Smalltalkでは、これを行うことができます。

|anAnimal|    
anAnimal := Pig new.
anAnimal makeNoise.

anAnimal := Cow new.
anAnimal makeNoise.

そのコード:

  1. anAnimalと呼ばれるローカル変数を宣言します(変数のTYPEは指定しないことに注意してください。すべての変数はオブジェクトへの参照であり、それ以上でもそれ以下でもありません。)
  2. 「Pig」という名前のクラスの新しいインスタンスを作成します
  3. そのPigの新しいインスタンスを変数anAnimalに割り当てます。
  4. makeNoiseブタにメッセージを送信します。
  5. 牛を使用して全体を繰り返しますが、豚と同じ正確な変数に割り当てます。

同じJavaコードは次のようになります(DuckとCowはAnimalのサブクラスであると想定しています:

Animal anAnimal = new Pig();
duck.makeNoise();

anAnimal = new Cow();
cow.makeNoise();

クラスVegetableを紹介するまでは、これで十分です。野菜は、動物と同じように振る舞いますが、すべてではありません。たとえば、動物と野菜の両方が成長する可能性があるかもしれませんが、野菜が騒々しくなく、動物を収穫できないことは明らかです。

Smalltalkでは、これを書くことができます:

|aFarmObject|
aFarmObject := Cow new.
aFarmObject grow.
aFarmObject makeNoise.

aFarmObject := Corn new.
aFarmObject grow.
aFarmObject harvest.

これは、Smalltalkでアヒル型になっているため、完全にうまく機能します(アヒルのように歩くと、アヒルのように鳴る-これはアヒルです)。この場合、メッセージがオブジェクトに送信されると、検索が実行されます。受信者のメソッドリスト。一致するメソッドが見つかると、それが呼び出されます。そうでない場合は、ある種のNoSuchMethodError例外がスローされますが、すべて実行時に行われます。

しかし、静的に型付けされた言語であるJavaでは、変数にどの型を割り当てることができますか?トウモロコシは成長をサポートするために野菜から継承する必要がありますが、動物から継承することはできません。牛はmakeNoiseをサポートするために動物から継承する必要がありますが、収穫を実装する必要がないため、野菜から継承することはできません。多重継承、つまり複数のクラスから継承する機能が必要なようです。しかし、すべてのエッジケースがポップアップするため、これはかなり難しい言語機能であることがわかります(複数の並列スーパークラスが同じメソッドを実装するとどうなるかなど)。

インターフェイスに沿って...

AnimalおよびVegetableクラスを作成し、それぞれがGrowableを実装する場合、牛が動物であり、トウモロコシが野菜であることを宣言できます。動物と野菜の両方が成長可能であることを宣言することもできます。それを書いて、すべてを成長させます:

List<Growable> list = new ArrayList<Growable>();
list.add(new Cow());
list.add(new Corn());
list.add(new Pig());

for(Growable g : list) {
   g.grow();
}

そして、動物の音を立てるためにこれを行うことができます:

List<Animal> list = new ArrayList<Animal>();
list.add(new Cow());
list.add(new Pig());
for(Animal a : list) {
  a.makeNoise();
}

アヒル型の言語の利点は、非常に優れた多態性が得られることです。動作を提供するためにクラスが実行する必要があるのは、メソッドを提供することだけです。全員が上手にプレイし、定義されたメソッドに一致するメッセージのみを送信する限り、すべてが問題ありません。欠点は、以下の種類のエラーが実行時まで検出されないことです。

|aFarmObject|
aFarmObject := Corn new.
aFarmObject makeNoise. // No compiler error - not checked until runtime.

静的に型付けされた言語は、コンパイル時に以下の2種類のエラーをキャッチするため、「コントラクトによるプログラミング」がはるかに優れています。

// Compiler error: Corn cannot be cast to Animal.
Animal farmObject = new Corn();  
farmObject makeNoise();

-

// Compiler error: Animal doesn't have the harvest message.
Animal farmObject = new Cow();
farmObject.harvest(); 

だから...要約すると:

  1. インターフェイスの実装では、オブジェクトが実行できる処理の種類(相互作用)を指定でき、クラスの継承では、処理の実行方法(実装)を指定できます。

  2. インターフェイスは、コンパイラの型チェックを犠牲にすることなく、「真の」ポリモーフィズムの多くの利点をもたらします。


2
これは別の質問に対する私の回答のテキストです:stackoverflow.com/questions/379282/…。しかし、それらは関連する答えです。
Jared、

2
だから、アヒル型の言語はどうやって動物の水()を区別するのですか?あいまいさは敵です。あいまいさを克服するために必要な詳細度は、許容できるIMOです。
ビルK

1
Yep..ambiguityは、アヒル型言語のゲームの名前です。アヒル型言語で専門的に作業する場合、長さが50〜100文字の名前のメンバー(メソッドと変数)が表示されることは珍しくありません。
Jared、

1
ダック型言語のもう1つの大きな欠点は、静的分析に基づいてプログラムによるリファクタリングを実行できないことです。Smalltalkイメージに、printStringメソッドのすべての呼び出し元のリストを要求してみてください...すべてのprintStringメソッドのすべての呼び出し元のリストが表示されます。 ...
Jared

... Automobile#printStringの呼び出し元は、NearEarthOrbit#printStringの呼び出し元とプログラムで区別できないためです。
Jared、

9

通常、インターフェースは、使用する必要があるインターフェースを定義します(名前が示すとおり;-))。サンプル


public void foo(List l) {
   ... do something
}

これで、関数fooは1つの型だけではなく、ArrayLists、LinkedLists、...を受け入れます。

Javaで最も重要なことは、複数のインターフェースを実装できるが、1つのクラスしか拡張できないことです。サンプル:


class Test extends Foo implements Comparable, Serializable, Formattable {
...
}
可能ですが

class Test extends Foo, Bar, Buz {
...
}
ではありません!

上記のコードは次のようになりますIBox myBox = new Rectangle();。ここで重要なことは、myBoxにはIBoxのメソッド/フィールドのみが含まれ、(おそらく存在する)他のメソッドは含まれないことRectangleです。


1
「リスト」はインターフェースメンバーであるはずですか?
2009

1
リストは、Javaコレクションライブラリのインターフェイスです。
rmeador 2009

リストは、標準Javaライブラリ(java.sun.com/javase/6/docs/api/java/util/List.html)のインターフェースです。彼は単にそれを彼のポイントを説明するために使用しています。
マイケルマイヤーズ

6

インターフェースのすべてを理解していると思いますが、インターフェースが役立つ状況についてはまだ想像していません。

オブジェクトをすべてインスタンス化、使用、および解放する場合、すべてを狭いスコープ内で(たとえば、1つのメソッド呼び出し内で)、インターフェースは実際には何も追加しません。あなたが指摘したように、具体的なクラスは知られています。

インターフェースが役立つのは、オブジェクトを1か所で作成して、実装の詳細を気にしない呼び出し元に返す必要がある場合です。IBoxの例をShapeに変更してみましょう。これで、Rectangle、Circle、TriangleなどのShapeの実装が可能になります。getArea()メソッドとgetSize()メソッドの実装は、具象クラスごとに完全に異なります。

これで、渡されたparamsに応じて適切なShapeを返すさまざまなcreateShape(params)メソッドでファクトリーを使用できます。明らかに、ファクトリーはどのタイプのShapeが作成されているかを認識しますが、呼び出し元にはありませんそれが円であるか、正方形であるかなどを気にします。

ここで、図形に対して実行する必要のあるさまざまな操作があるとします。おそらく、それらを領域でソートし、すべてを新しいサイズに設定して、UIに表示する必要があります。Shapesはすべてファクトリによって作成され、ソーター、サイザー、ディスプレイクラスに簡単に渡すことができます。将来六角形クラスを追加する必要がある場合は、ファクトリーを変更する必要はありません。インターフェイスがないと、別の形状を追加するのが非常に面倒なプロセスになります。


6

あなたはできる

Ibox myBox = new Rectangle();

このようにして、このオブジェクトをIboxとして使用していて、それが本当に気にならないようにしますRectangle


つまり、このように書くことができますか?!> Rectangle inst = new Rectangle();
Dr.jacky

@ Mr.Hyde後で追加しSquareたい場合は問題が発生します...インターフェイスなしで実行しようとするSquareRectangle、同じ方法を使用していることを保証できません...これは、より大きなコードベース...インターフェイスはテンプレートを定義することを覚えておいてください。
Kolobキャニオン

6

なぜインターフェース??????

それは犬から始まります。特にパグ

パグにはさまざまな動作があります。

public class Pug { 
private String name;
public Pug(String n) { name = n; } 
public String getName() { return name; }  
public String bark() { return  "Arf!"; } 
public boolean hasCurlyTail() { return true; } }

そして、あなたには一連の行動をするラブラドールがいます。

public class Lab { 
private String name; 
public Lab(String n) { name = n; } 
public String getName() { return name; } 
public String bark() { return "Woof!"; } 
public boolean hasCurlyTail() { return false; } }

私たちはいくつかのパグとラボを作ることができます:

Pug pug = new Pug("Spot"); 
Lab lab = new Lab("Fido");

そして、それらの動作を呼び出すことができます:

pug.bark() -> "Arf!" 
lab.bark() -> "Woof!" 
pug.hasCurlyTail() -> true 
lab.hasCurlyTail() -> false 
pug.getName() -> "Spot"

犬小屋を経営していて、飼っているすべての犬を追跡する必要があるとします。私は別々の配列で私のパグとラブラドールを保存する必要があります

public class Kennel { 
Pug[] pugs = new Pug[10]; 
Lab[] labs = new Lab[10];  
public void addPug(Pug p) { ... } 
public void addLab(Lab l) { ... } 
public void printDogs() { // Display names of all the dogs } }

しかし、これは明らかに最適ではありません。いくつかのプードル収納したい場合、犬舎の定義を変更してプードルの配列を追加する必要があります。実際、犬の種類ごとに個別の配列が必要です。

洞察:パグとラブラドール(およびプードル)はどちらも犬の一種であり、同じ動作セットを持っています。つまり、(この例の目的で)すべての犬は吠え、名前を付けることができ、巻き毛の尾がある場合とない場合があると言えます。すべての犬ができることを定義するためにインターフェースを使用できますが、それらの特定の動作を実装するために特定のタイプの犬に任せます。インターフェースは「すべての犬ができることはここにある」と述べていますが、それぞれの行動がどのように行われるかについては述べていません。

public interface Dog 
{
public String bark(); 
public String getName(); 
public boolean hasCurlyTail(); }

次に、PugクラスとLabクラスを少し変更して、Dogの動作を実装します。パグは犬で、ラボは犬であると言えます。

public class Pug implements Dog {
// the rest is the same as before } 

public class Lab implements Dog { 
// the rest is the same as before 
}

以前と同じようにPugsとLabsをインスタンス化することもできますが、今ではそれを実行する新しい方法も利用できます。

Dog d1 = new Pug("Spot"); 
Dog d2 = new Lab("Fido");

これは、d1は犬だけではなく、具体的にはパグであることを示しています。また、d2も犬、特にラボです。ビヘイビアーを呼び出すことができ、ビヘイビアーは以前と同様に機能します。

d1.bark() -> "Arf!" 
d2.bark() -> "Woof!" 
d1.hasCurlyTail() -> true 
d2.hasCurlyTail() -> false 
d1.getName() -> "Spot"

ここですべての余分な作業が報われます。Kennelクラスがはるかに単純になります。1つの配列と1つのaddDogメソッドだけが必要です。どちらも犬であるオブジェクトで動作します。つまり、Dogインターフェースを実装するオブジェクトです。

public class Kennel {
Dog[] dogs = new Dog[20]; 
public void addDog(Dog d) { ... } 
public void printDogs() {
// Display names of all the dogs } }

使用方法は次のとおりです。

Kennel k = new Kennel(); 
Dog d1 = new Pug("Spot"); 
Dog d2 = new Lab("Fido"); 
k.addDog(d1); 
k.addDog(d2); 
k.printDogs();

最後のステートメントは次のように表示されます:Spot Fido

インターフェイスを使用すると、インターフェイスを実装するすべてのクラスが共有する一連の動作を指定できます。その結果、変数やコレクション(配列など)を定義することができます。これらは、インターフェイスを実装するオブジェクトを保持するだけで、それらが保持する特定のオブジェクトの種類を事前に知る必要はありません。


@niranjan kurambhatti犬を拡張するためにすべてのクラスを作成できますが、それでもなぜインターフェースですか?
Jeeva

3

インターフェイスがどのように使用されるかの良い例は、コレクションフレームワークにあります。あなたがとる関数を記述する場合List、ユーザーがで通過すれば、それは問題ではありませんVectorか、ArrayListまたはHashListまたはものは何でも。そしてListCollectionそれをまたはを必要とするすべての関数に渡すことができますIterableインターフェースをます。

これによりCollections.sort(List list)、のList実装方法に関係なく、のような機能が可能になります。


3

これが、Javaでファクトリパターンやその他の作成パターンが非常に人気がある理由です。それらがなければ、Javaはインスタンス化を簡単に抽象化するためのすぐに使えるメカニズムを提供しないことは正しいです。それでも、メソッド内にオブジェクトを作成しない場所であればどこでも抽象化されます。これはほとんどのコードです。

余談ですが、私は一般的に、インターフェイスに名前を付けるために「IRealname」メカニズムに従わないことをお勧めします。それはWindows / COMのことであり、ハンガリー語の表記法の真っ只中にあり、実際には必要ありません(Javaはすでに強く型付けされており、インターフェイスを持つことの重要な点は、それらをクラス型とできるだけ区別できないようにすることです)。


1
強い型付けと静的型付けを混同しています。
eljenso

3

後日、既存のクラスを取得して実装できることを忘れないでください。IBoxそうすると、ボックス対応のすべてのコードで使用できるようになります。

これは、インターフェースが-ableと名付けられている場合、少し明確になります。例えば

public interface Saveable {
....

public interface Printable {
....

など(命名スキームは常に機能するとは限りません。たとえば、Boxableここで適切かどうかはわかりません)


3

インターフェイスの唯一の目的は、インターフェイスを実装するクラスが、そのインターフェイスに記述されている正しいメソッドを持っていることを確認することです。または、インターフェースの他の用途はありますか?

Java 8で導入されたインターフェイスの新機能で回答を更新していますバージョンで。

インターフェースの要約に関するオラクルのドキュメントページから:

インターフェース宣言には

  1. メソッドシグネチャ
  2. デフォルトのメソッド
  3. 静的メソッド
  4. 定数の定義。

実装を持つ唯一のメソッドは、デフォルトの静的メソッドです。

インターフェイスの使用

  1. 契約を定義するには
  2. 関係のないクラスをリンクする機能には、(実装するクラスなど)Serializableインターフェイスをするそのインターフェイスを実装することを除いて、間に関係がある場合とない場合があります。
  3. 戦略パターンなど互換性のある実装を提供する
  4. デフォルトのメソッドを使用すると、ライブラリのインターフェースに新しい機能を追加し、それらのインターフェースの古いバージョン用に作成されたコードとのバイナリ互換性を確保できます
  5. 静的メソッドを使用して、ライブラリ内のヘルパーメソッド整理します(別のクラスではなく、同じインターフェイスのインターフェイスに固有の静的メソッドを保持できます)

抽象クラスインターフェースの違いに関するいくつかの関連するSEの質問と実際の例を使用する場合:

インターフェイスと抽象クラスの違いは何ですか?

インターフェイスと抽象クラスの違いをどのように説明すればよいですか?

ドキュメントページを見て、Java 8に追加された新機能、デフォルトメソッドと静的メソッドを理解してください。


質問ではjava-8について何も質問されなかったため(そして実際にはjava-8よりもずっと前に質問されたため)、java-8タグを削除しました。タグは質問用であり、回答用ではありません。
Tagir Valeev

2

インターフェースの目的は、抽象化、つまり実装から分離することです。

プログラムに抽象化を導入する場合、可能な実装について気にする必要はありません。あなたは、に興味があるそれを行うことができず、どのように、あなたが使用してinterfaceJavaでこれを表現します。


すべての構造化プログラミングの目的は抽象化です。ジェネリックとクラス構成を使用してまったく同じことを実現できるので、インターフェイスの目的が抽象化であると言うのはなぜですか?
Apocalisp、2009

1
すべての構造化プログラミングが抽象化(あなたの主張)である場合、インターフェースはその抽象化における抽象化です。
eljenso

1

CardboardBoxとHtmlBox(どちらもIBoxを実装)がある場合、IBoxを受け入れる任意のメソッドに両方を渡すことができます。それらは非常に異なり、完全に交換可能ではありませんが、「開く」または「サイズ変更」を考慮しないメソッドでもクラスを使用できます(おそらく、画面に何かを表示するために必要なピクセル数を考慮しているためです)。


1

多重継承を可能にするために機能がJavaに追加されたインターフェース。しかし、Javaの開発者は、多重継承を持つことは「危険な」機能であることを認識しました。そのため、はインターフェースのアイデアを思いつきました。

次のようなクラスがある可能性があるため、多重継承は危険です。


class Box{
    public int getSize(){
       return 0;
    }
    public int getArea(){
       return 1;
    }

}

class Triangle{
    public int getSize(){
       return 1;
    }
    public int getArea(){
       return 0;
    }

}

class FunckyFigure extends Box, Triable{
   // we do not implement the methods we will used the inherited ones
}

これは、使用時に呼び出す必要があるメソッドです。


   FunckyFigure.GetArea(); 

すべての問題はインターフェイスで解決されます。これは、インターフェイスを拡張できること、およびそれらにクラス化メソッドがないことがわかっているためです。もちろん、コンパイラーは素晴らしく、メソッドを実装しなかったかどうかを通知しますが、私はそうだと思いますより興味深いアイデアの副作用。


回答で複数の実装の継承と複数のインターフェースの継承を区別したい場合があります。そうしないと混乱を招きます。
eljenso

0

これがインターフェースの利点に関する私の理解です。私が間違っていたら訂正してください。私たちがOSを開発していて、他のチームがいくつかのデバイスのドライバーを開発していると想像してください。そこで、インターフェイスStorageDeviceを開発しました。他の開発チームが提供する2つの実装(FDDとHDD)があります。

次に、StorageDeviceインターフェイスを実装したクラスのインスタンスを渡すだけで、saveDataなどのインターフェイスメソッドを呼び出すことができるOperatingSystemクラスがあります。

ここでの利点は、インターフェースの実装を気にしないことです。他のチームは、StorageDeviceインターフェースを実装することでこの仕事を行います。

package mypack;

interface StorageDevice {
    void saveData (String data);
}


class FDD implements StorageDevice {
    public void saveData (String data) {
        System.out.println("Save to floppy drive! Data: "+data);
    }
}

class HDD implements StorageDevice {
    public void saveData (String data) {
        System.out.println("Save to hard disk drive! Data: "+data);
    }
}

class OperatingSystem {
    public String name;
    StorageDevice[] devices;
    public OperatingSystem(String name, StorageDevice[] devices) {

        this.name = name;
        this.devices = devices.clone();

        System.out.println("Running OS " + this.name);
        System.out.println("List with storage devices available:");
        for (StorageDevice s: devices) {
            System.out.println(s);
        }

    }

    public void saveSomeDataToStorageDevice (StorageDevice storage, String data) {
        storage.saveData(data);
    }
}

public class Main {

    public static void main(String[] args) {

        StorageDevice fdd0 = new FDD();
        StorageDevice hdd0 = new HDD();     
        StorageDevice[] devs = {fdd0, hdd0};        
        OperatingSystem os = new OperatingSystem("Linux", devs);
        os.saveSomeDataToStorageDevice(fdd0, "blah, blah, blah...");    
    }
}

同じことは、抽象クラスStorageDeviceとFDDおよびStorageDeviceクラスを拡張するHDDクラスで行うことができます。しかし、抽象クラスを使用する場合、多重継承を利用できません。
Vladimir Georgiev
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.