#はじめに
スキルアップのため、これからは勉強したことをQiitaに投稿していきます。
今回はJavaの多態性についてです。
JavaもQiitaも超がつく初学者のため、間違いがあるかもしれません。その時は教えてくださると助かります。
######使用言語とOS
この記事ではWindowsにインストールしたJava11.0.6を使っています。
#多態性とは
オブジェクトを大まかにとらえることで、開発をしやすくする機能のことです。
例えば、「バラ」「ひまわり」「チューリップ」は細かく言うとそれぞれの育て方は異なるものの、水をあげれば花が咲く点は共通しています。
なので、私たちはこの3種類を「花」という大まかなとらえ方をしていて、「花」だとわかればとりあえず水をやるわけです。このとらえ方をプログラムでもしようっていうのが多態性です。
#プログラム内で多態性を利用する
特別な書き方はなく、代入の書き方をすればOKです。
######代入ってこんなやつ
int num1 = 100;
int num2 = 200;
num1 = num2;
String str = "多態性";
変数に変数とかリテラルを入れることですね。
実際書いてみると、こんな感じになります。
Flower f2 = new Rose();
Flowerというクラス型にRoseというクラスが入っています。つまり、Flowerの箱にRoseインスタンスが入っているわけです。細かく言うとRoseなんだけど、大まかにFlowerとしてとらえてねってことです。
以下は多態性を正しく利用できていない例です。
Flower f2 = new Doctor();
Rose f3 = new Flower();
まず1行目を見てください。Flowerの箱にDoctorインスタンスが入っているのはおかしいですね。医者は花ではありません。
2行目のコードについては、クラス名が逆になってしまっています。この箱にはRoseが入っていますと言われて中身を見たらFlowerという抽象的なものが入ってました、みたいなイメージです。
正しく多態性を利用するコツとしては、「is-a」の関係になっているかを見ることがあります。
「作ったインスタンスのクラス名 is a 変数のクラス名」になっているかを見ましょう。今回なら「Rose is-a Flower」が正しい関係です。
#多態性を利用すると何が起きるか
###呼び出し可能なメソッドの変化
多態性によってインスタンスを大まかにとらえることができました。
これを利用すれば、「バラ」「ひまわり」「チューリップ」それぞれに「朝8時になったときに、もし水が与えられたら吸収して花を咲かす。与えられなければ…」といちいち指示(メソッド)をしなければいけなかったものが、「花」という大まかなもの一つに指示をすればそこに入っているインスタンスが各々の役割を自ら果たしてくれるようになります。
一方で、大まかなものとしてとらえることで以下のような困りごとが出てきます。
下の2つのコードを見てください。
public abstract class Flower {
String name;
String color;
public void bloom() {
System.out.println("this.name + "は" + this.color + "色に咲きました");
}
}
public class Rose extends Flower {
int thron;
public void cutThorn() {
System.out.println(this.name + "のトゲを" + this.thron + "個切りました");
}
RoseはFlowerのクラスと基本的には同じですが、トゲがある点において特徴を持ちます。
ですので、コードとしてextendsを用いてFlowerを継承し、かつ自身の特徴であるトゲとそれに対するメソッドが書かれています。
イメージとしては、箱の種類はFlowerでその中にはRoseが入っている感じです。
Roseをインスタンス化すれば、もちろんですがcutThron()メソッドを使えるはずです。実際に使ってみましょう。
Rose r = new Rose();
Flower f = r;
f.bloom();
f.cutThron();
…実は、このコードだと3行目までは順調にいきますが、4行目でコンパイルエラーになってしまいます。
確かにRoseインスタンスを生成したはずなのにおかしいですね。。
こうなってしまう原因は、多態性を利用した1行目にあります。というのも、コンピュータはFlowerという箱の中に花の一種が入っていることしかわからないのです。箱に入られてしまうと、中身が見えなくなってしまうんですね。
つまり、Flowerクラスにはbloom();メソッドがもともとありますので、コンピュータからすればFlowerという箱に入っているからとりあえずbloom()は使えるなということは判断できます。しかし、箱の詳細な中身(花の種類)まではわからないので、Flowerの箱に入っているインスタンスがcutThron()が使えるRoseインスタンスであることを判断できません。そのため、確実でないことを実行できないということでコンパイルエラーになってしまうのです。
#多態性を利用するメリット
ここまでだと、多態性って使えるメソッドが減って不便になっているのではと思ってしまいます。
ですが、多態性にはこの不便さを超えるメリットがあります。この項では、そのメリットについて記載していきます。一つ目は配列と組み合わせるとコードがすっきりすること、二つ目は1つの命令だけで各インスタンスがそれぞれの仕事を果たしてくれることです。
具体例を見ていきます。以下のコードでは、Rose・SunFlower・TulipはFlowerを継承しており、Flowerにはbloom()メソッドがあると考えてください。
###配列と組み合わせる
まずは、多態性を利用せずにコードを書くと以下のようになります。
public class Main {
poublic static void main(String[] args) {
Rose r = new Rose();
r.bloom();
SunFlower s = new SunFlower();
s.bloom();
Tulip t = new Tulip;
t.bloom();
そして、次が多態性を利用して書いたコードです。
public class Main {
public static void main(String[] args) {
Flower[] f = new Flower[3];
f[0] = new Rose();
f[1] = new SunFlower();
f[2] = new Tulip();
for (Flower fl : f) {
fl.bloom();
}
}
}
…今回の例では大して行数が変わらずすっきりした印象を受けませんが笑、今後花の種類が増えたり追加したいメソッドが増えた場合、前者の書き方だとどんどん行数が増えていってしまいます。また、変数がいくつもあると管理が大変になってしまう可能性もあります。
ですので後者のように、Flower配列の中にRoseなどのインスタンスをまとめてそれらをFlowerとして大まかにとらえることで、今後花の種類やメソッドが増えても要素を追加するだけで済むので、コードをすっきりと書くことができます。
###大まかにとらえても各インスタンスごとの仕事を果たす
先ほどまではbloom()の内容はどのインスタンスも同じになっていました。しかし実際は、花によって咲き方はそれぞれです。例えばRoseなら1つの茎が何度も分岐して複数の花を咲かせますが、SunFlowerなら1本の茎に1つの花だけが咲きます。それを踏まえ、各インスタンスごとにbloom()をオーバーライド(上書き)したとしましょう。
本来ならば、Main.javaにはインスタンスの数だけbloom()を書かなければいけません。ですが、多態性によりFlowerでまとめていれば、私たちは1度bloom()を命令するだけで、あとは各インスタンスが自身のbloom()を実行してくれます。
これについては、コードを見るとよりわかりやすいかもしれません。こちらからの命令はbloom()1回だけですが、これを実行するとRoseインスタンスは茎を分岐させて複数個の花を咲かせ、SunFlowerインスタンスは茎を分岐させずに大きな花を1つだけ咲かせるように、自身固有の咲き方をします。
public class Main {
public static void main(String[] args) {
Flower[] f = new Flower[3];
f[0] = new Rose();
f[1] = new SunFlower();
f[2] = new Tulip();
for (Flower fl : f) {
f1.bloom();
}
}
}
つまり、私たちはRoseもSunFlowerも大まかにFlowerとしてとらえて「咲け!」と命令を行ったとしても、RoseやSunFlowerは自身に定められた固有の咲き方で咲いてくれるわけです。
#まとめ
「多態性」を一言でいえば、命令する側の私たち人間が楽をできる素晴らしい機能って感じでしょうか。笑
一度勉強しただけではいまいち頭に入りづらい難しい機能ですので、「is-a」や箱のクラス型を意識して何度もコードを書いてみたいと思います。それと、Qiitaにも早く慣れたいところです。
ここまで見てくださりありがとうございました。