多態と static
このレッスンで分かること
- 同じ名前のメソッドを種類ごとに用意すると、使う側から
ifが消えますstaticを付けたものはインスタンスではなくクラスに付き、値がそのまま残り続けます
呼ぶ側の if が、種類の数だけ増える
前の回で、種類ごとにクラスを分けました。分けたあと、使う側がこう書いていると苦しくなります。
JavaScript
const lines = notes.map((note) => {
if (note instanceof PinnedNote) {
return "重要 " + note.topic + " " + note.minutes + " 分";
}
return note.topic + " " + note.minutes + " 分";
});種類が 3 つになれば分岐が 3 本並びます。しかも同じ分岐を、一覧の描画、件数の集計、並び替えと、場所ごとに書き写すことになります。1 か所を直し忘れると、そこだけ古い表示が残ります。
同じ名前のメソッドを両方のクラスに持たせておけば、分岐はクラスの側に 1 回書くだけで済みます。
JavaScript
const lines = notes.map((note) => note.render());どちらの render が動くかは、そのインスタンスがどのクラスから作られたかで自動的に決まります。呼ぶ側は種類を知らなくてよくなります。
効くのは、メソッド名が完全に同じで、引数と戻り値の形もそろっているときだけです。片方が文字列を返し、もう片方がオブジェクトを返すなら、結局呼ぶ側で分岐することになります。名前だけそろえても意味がありません。
static はインスタンスではなくクラスに付く
ここまでのメソッドは new で作ったインスタンスから呼ぶものでした。static を付けると、クラス名から直接呼ぶものになります。
JavaScript
class Comment {
static total = 0;
constructor(body) {
this.body = body;
Comment.total += 1;
}
static countLabel() {
return `これまでに ${Comment.total} 件`;
}
}
new Comment("はじめまして");
new Comment("参考になりました");
console.log(Comment.total); // 2
console.log(Comment.countLabel()); // これまでに 2 件| 書き方 | 誰に付くか | 呼び方 |
|---|---|---|
render() {} | インスタンス | note.render() |
static countLabel() {} | クラス | Comment.countLabel() |
static total = 0 | クラス | Comment.total |
total は Comment に 1 つだけあります。comment.total と書いても undefined です。インスタンスからは見えません。
static のメソッドは new より前に呼べるので、「どのクラスで作るか」の判断をクラス自身に持たせる窓口にもできます。呼ぶ側は子クラスの名前を知らずに済み、あとで種類が増えても直すのはその窓口の中だけです。
static に置いた数は、次の呼び出しにも残る
static のフィールドは、プログラムが動いている間ずっと生き続けます。同じ関数を 2 回呼ぶと、2 回目は前回に増えた分を引きずります。
JavaScript
console.log(Comment.total); // 2
new Comment("また来ました");
console.log(Comment.total); // 3第3章で、何回呼んでも同じ結果になる関数のことを学びました。static を使うとその性質は簡単に崩れます。数え直したいなら、数える処理の先頭で 0 に戻すと決めておきます。戻す場所を末尾に置くと、途中で return したときに素通りします。
staticに持たせてよいのは、クラス全体で 1 つと決まっているものだけです。作った件数や既定の設定値がそれにあたります。個々のインスタンスに関わる値を置くと、あとから作ったほうに上書きされます。
static メソッドを生成の窓口にする
クラス名から呼ぶメソッドで、受け取った値から本文を組み立ててインスタンスを作れます。本文の Comment に次の窓口を加える例です。
JavaScript
static greeting(name) {
const body = "ようこそ、" + name;
return new Comment(body);
}
// クラスの外から Comment.greeting("さくら") と呼ぶ演習では準備済みの Work.from(record) の中で、どのクラスを生成するかを決めます。
よくある間違い
- インスタンスから
staticを呼ぶ —comment.countLabel()はTypeErrorです。staticはクラスに付いているので、クラス名から呼びます staticの中のthisをインスタンスのつもりで使う —staticのthisはクラス自身です。this.bodyと書いてもundefinedにしかなりません- 上書きしたつもりでメソッド名を間違える —
renderをrendarと書いても、その場ではエラーになりません。子のインスタンスで呼ぶと親のほうが静かに動き、印の付かない行だけが混ざります
要件
- Work に static created を 0 で宣言し、constructor で Work.created を 1 増やす
- static from(record) に、views が 100 以上なら PopularWork を返す分岐を加える。それ以外の Work の生成は準備済み
- PopularWork の format() は親の表示の後ろに半角スペースと 人気 を足して返す
- buildWorkReport(records) の先頭で Work.created を 0 に戻す。静的メソッドの呼び出しと戻り値の整形は準備済み