19.0430

インターフェイス(interface)

インターフェイスとは、抽象クラス と、似ています。
が、違うところも、あります。
まだ、インターフェイス とは、なんなのだ、という話もまだなのに、
とりあえず、違いから見ていきましょう。
インターフェイスとは何か、に関しては、実際にコードを見ながら見ていきます。

インターフェイス と、抽象クラス の違い
  1. プロパティは、実装できない
     抽象クラスの場合、メソッド も、プロパティ も、実装出来ます。
    (実装できるとは、プロパティ の場合は、値を決めたコードを書くこと.
     メソッドの場合は、{ 具体的なコード } を、書くこと.)
     ただし、最低1つは、abstract な プロパティ か メソッド を、書くこと

     しかし、インターフェイス のばあい、メソッドは、実装出来ますが、
     プロパティは、実装出来ません

  2. 多重継承ができる
     多重継承 とは、「2つ以上のクラスから、継承すること」 です。
     interface は、それが出来るのですが、interface は classではないからです。
     class を使った、多重継承 は、出来ません。
     interface は、class では、ありませんが、構造的に見たら、class とも言えそうです。
     ただ、実装されている部分が、少ないので、継承先に、
     「やり過ぎる/余計なプロパティ・メソッドを付け加える」ことは、ありません。
     それで、多重継承の問題点である、「複雑になりすぎる」 ことから逃れられます。
     
     では、どんな時に、多重継承 したいでしょうか?
     これは、ゲームの世界でしかありえないでしょうが、
     たとえば、「空飛ぶくらげclass」 と、「空飛ぶ車class」 を作るとします。
     この場合、「くらげclass」 と 「車class」 のそれぞれに、「空飛ぶ機能」 を、実装するより、
     「空飛ぶinterface」 を、実装/継承という言葉は使いません した方が、
     効率的かもしれません(interface は、メソッドを実装出来ます)。
     また、「空飛ぶくらげclass」 と 「空飛ぶ車class」 と 「空飛ぶ猫class」 であれば、
     これはもう、「空飛ぶinterface」 を、実装した方が、まず、効率的でしょう。
     そればかりでなく、実装したクラスの 構造/仕組み も、よりクリアになるでしょう。


interface を、実装したコード例
そこで、こんなコードを書いてみました
このコードの概要:
まず、何をするのか?は、
  紹介
  鳴く
を、ねこ、いぬ、きつね に、やっていただきます。
それで、
  抽象class Animal をつくり、Cat/Dog/Foxクラス を、継承して作ります。
  インターフェイスmakeSound を、それぞれのclassに、実装します。
    / これは、楽器でも、人間でも、一応は使えます。
こんなところですが、このコードのポイントは、
  1. 抽象class の、継承(Animal ⇐ Cat/Dog/Fox)
  2. それによって、多態性が、使える(introduceAndShout(a: Animal)関数)。
  3. MakeSoundインターフェイス の実装

このコードに関して、新しい部分は、
  interface の、実装方法
だけです。
ですので、簡単に見ていきます。

2~18行目:
  インターフェイスMakeSoundの定義
    プロパティ は、実装出来ません:
      val sound: String(音のStringインスタンス)
      val howMany: Int(何回、音を発するか、Int型)
        / interface は、必ず実装します、、openキーワード は、不要です。
    メソッドは、実装します。
      このコードの場合は、ここで実装しておいた方が、楽です。
      makeSound(sound: String, howMany: Int)
        / プロパティ、sound・howMany を使って、sound を、howMany回、発します。

20~22行目:
  抽象class Animal の定義
    プロパティ を、abstract にする。
      abstract val kind: String(kind:動物の種類、String型)
    この場合、各クラスに、含めた方が、簡単な気もしますが、
    この利点は、この親class を、作っておくと、多態性を使うとき、Animal型 として使えます。

24~38行目、40~48行目、50~58行目:
  それぞれ、Cat/Dog/Foxクラス を、
    抽象クラスAnimal、インターフェスmakeSound
  の、overrideすべきを、実装します。
  introduce()メソッド は、それぞれ、新たに書いています。
    / これは、同じ内容なので、抽象class で、実装しておくべきでした。
    / まあでも、こんな(軽い)失敗例も、見ておくことは、勉強になりますね。
      / つまり、Animalクラス で、実装しておけば、3回も同じことを書かなくて済みます。
  makeSound()メソッド は、インターフェイスMakeSound のを、そのまま使いますので、
  何も書かなくても、実装されます。
  インターフェイスの実装方法 は、抽象クラス の、場合と違って、
  コンストラクタ/() は、付けません!
  ここには、注意してください。
    class Cat(val name: String, val color: String) : Animal(), MakeSound
  また、2つ目の実装からは、, で、区切ってください。

61~63行目:main()関数:
  Cat/Dog/Foxクラス のインスタンスcat/dog/fox を、作っています。

65~85行目:main()関数:
  Cat/Dog/Foxクラス の インスタンス、cat/dog/fox は、
  Animal型 として、扱えるので(多態性)
  そのための、関数(classに属していないのでメソッドではない)
    introduceAndShout(a: Animal)(紹介して 鳴く(叫ぶ))
  を、作っています。
  when文 を、使いました。
  when式 では、else が、必須ですが、when文 の時は、なくても、OKです。

82~87行目:main()関数:
  1. println() を使って、まず動物の種類を出力します。
    cat.kind のようにして、それぞれのclassの、プロパティ に、アクセスし、
    それを、文字列テンプレート に、まず返し、他の文字/: と、一緒に、コンソールに、出力します。
  2. introduceAndShout(a: Animal)メソッド に、
    実際の型(cat/dog/fox)(大雑把にAnimal型ではなくて)を、引数にして、実行しています。
  3. 以上の「1.2.パターン」 を、3回行っています。

以上を踏まえて、コンソールへの出力がどうなるか、考えてください。
結果はこうなります



まとめ
interface/インターフェイス では、メソッドは、実装出来ますが、
 プロパティは、実装出来ません
・ interface は、多重継承 が出来ます。(class/抽象class を、2つ以上、実装出来ません)
・ interface の、実装時、コンストラクタ は、付けません!
・ class に、2つ以上、実装する時は , で、区切ります。
・ interface での、プロパティ・実装されていないメソッド は、実装先のclass で、実装しなければならない。