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();
}

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

4

17 に答える 17

142

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

Ibox myBox=new Rectangle();

その後、後で別の種類のボックスを使用することにした場合 (より良い種類のボックスを備えた別のライブラリがある可能性があります)、コードを次のように切り替えます。

Ibox myBox=new OtherKindOfBox();

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

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

myBox.close()

(IBox に close() メソッドがあると仮定します) myBox の実際のクラスは、繰り返しのどのボックスにあるかによって変わります。

于 2009-02-02T21:12:59.933 に答える
125

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

真意はすでにその名前にあります。これらは、誰でも実装できるインターフェイスを定義し、そのインターフェイスで動作するすべてのコードを使用できるようにします。最良の例は、やforjava.util.Collectionsなどのインターフェイスで排他的に動作するあらゆる種類の便利なメソッドを提供するものです。ここでのポイントは、このコードを使用して、インターフェイスを実装する任意のクラスを並べ替えたり反転したりできるようになったことです。 と だけでなく、自分で作成したクラスも作成できます。これは、書いた人が想像もしなかった方法で実装される可能性があります。sort()reverse()ListListArrayListLinkedListjava.util.Collections

同様に、よく知られたインターフェイスや自分で定義したインターフェイスで動作するコードを書くことができ、他の人は自分のクラスのサポートを頼まなくても自分のコードを使うことができます。

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

于 2009-02-02T22:03:38.083 に答える
120

私が読んだ多くの用途の 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 
} 

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

interface Chewable {
void chew();
}

そして、鳥ではなく爬虫類クラスにこれを実装させます(鳥は噛むことができないため):

class Reptile extends Animal implements Chewable { } 

単純に鳥の場合:

class Bird extends Animal { }
于 2013-06-20T17:46:18.930 に答える
48

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

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

メソッドを呼び出すときは、インターフェイスscaleを実装する型の任意の値を指定できIBoxます。言い換えればRectangleSquare両方とも を実装する場合、が期待される場所ならどこでも、またはIBoxを提供できます。RectangleSquareIBox

于 2009-02-02T21:14:48.057 に答える
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をサポートするために動物から継承する必要がありますが、収穫を実装する必要がないため、野菜から継承することはできません。多重継承、つまり複数のクラスから継承する機能が必要なようです。しかし、ポップアップするすべてのエッジケースがあるため、これはかなり難しい言語機能であることがわかります(複数の並列スーパークラスが同じメソッドを実装するとどうなりますか?など)。

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

動物と野菜のクラスを作成し、それぞれが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. インターフェイスは、コンパイラの型チェックを犠牲にすることなく、「真の」ポリモーフィズムの多くの利点を提供します。

于 2009-02-02T21:27:32.477 に答える
9

通常、インターフェイスは使用するインターフェイスを定義します (名前が示すように ;-) )。サンプル


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

これで、関数はs、s、... を 1 つの型だけでなくfoo受け入れるようになりました。ArrayListLinkedList

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


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

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

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

于 2009-02-02T21:14:07.700 に答える
6

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

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

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

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

ここで、シェイプに対してさまざまな操作を実行する必要があるとします。それらを領域ごとに並べ替え、すべてを新しいサイズに設定してから、UI に表示する必要があるかもしれません。Shapes はすべてファクトリによって作成され、Sorter、Sizer、および Display クラスに非常に簡単に渡すことができます。将来、hexagon クラスを追加する必要がある場合、ファクトリ以外は何も変更する必要はありません。インターフェースがなければ、別の形状を追加するのは非常に厄介なプロセスになります。

于 2009-02-02T21:58:50.090 に答える
6

あなたができる

Ibox myBox = new Rectangle();

そうすれば、このオブジェクトを Ibox として使用することができ、そのオブジェクトが本当にRectangle.

于 2009-02-02T21:11:03.510 に答える
3

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

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

public interface Saveable {
....

public interface Printable {
....

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

于 2009-02-03T11:10:35.857 に答える
3

インターフェイスがどのように使用されるかの良い例は、コレクション フレームワークにあります。を受け取る関数を作成する場合、ユーザーが渡すのがか か かなどはList問題ではありません。またはインターフェイスを必要とする任意の関数にそれを渡すこともできます。VectorArrayListHashListListCollectionIterable

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

于 2009-02-02T21:14:57.947 に答える
3

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

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

インターフェイスの概要に関するOracleドキュメントページから:

インターフェイス宣言には次を含めることができます

  1. メソッド署名
  2. デフォルトの方法
  3. 静的メソッド
  4. 定数の定義。

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

インターフェイスの使用:

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

抽象クラスインターフェースの違い、および実際の例を使用したユースケースに関する関連する SE の質問:

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

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

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

于 2016-03-04T21:16:07.453 に答える
2

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

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

于 2009-02-04T14:54:30.397 に答える
1

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

于 2009-02-02T21:14:26.200 に答える
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(); 

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

于 2009-02-02T21:17:17.570 に答える