Part19: ジェネリクス

ことり「スクフェス大型アップデート、くるみたいだね」
凛「EXですらフルコンつらいのにMASTERとか頭おかしいにゃ」
ことり「まあまあ、EX常設になるから、そこで鍛えれば・・・」
凛「あ、シールで覚醒できるのは嬉しいにゃ」
ことり「カード増えすぎてURとか限りなく無理だったもんね」
凛「Aqoursも本格参戦だね」
ことり「そうそう、頑張ってAqoursメンバー全員揃えたんですよ~。シールショップで」
凛「7月からは勧誘でも出るんだよね」
ことり「Aqoursのイベントもあるみたいだね。最初はやっぱり千歌ちゃんかな?」
凛「曜ちゃんの可能性も・・・。それらしき発言したのしゅかしゅーだったし」
ことり「もちろん、私たちμ’sの展開もまだまだ続きます。請う、ご期待♪」


ことり「さて今回は、ジェネリクスのお話ですよ」

ことり「配列を作るとき、こうやってStringの配列です、って指定をしたよね」
凛「Array<Int>とかも書けたにゃ」
ことり「これと同じように、クラスに型をパラメータとして渡すことができます。これがジェネリクス」

ことり「たとえばこんな使い方。Trioってクラスがジェネリッククラスで、Tっていう型パラメータを取ります」
凛「えーと、Trio<SchoolIdol>ってするとTSchoolIdolになるから、class Trio(center: SchoolIdol, left: SchoolIdol, right: SchoolIdol)になるの?」
ことり「そうそう。同じようにTProfessionalIdolにすればプライマリコンストラクタの引数は全部ProfessionalIdolになるの」
凛「1つのクラスでトリオ漫才でもこの前増えたアルパカでも扱えるんだね」


宣言側変性

ことり「さて、ジェネリッククラスが受け取った型パラメータの使われる箇所は、入力、出力、あるいはその両方のいずれかになります」
凛「・・・よくわからないにゃ」
ことり「入力は、引数として受け取る値。出力は、戻り値として返す値。たとえば」

ことり「Sourceでは、T型の値を戻り値として返すだけで、引数で受け取って利用するわけじゃないよね」
凛「それが、出力?」
ことり「うん。なんだけど、このコードはコンパイルできないんだ」
凛「えーっと・・・Source<Any>Source<String>の互換性がなんとか」
ことり「そう。入力が伴うとしたら互換性がなくなっちゃうんだけど、出力だけなら本来は問題ないよね。Stringを返すところがAnyを返すようになるだけだから」
凛「これ、どうしたらいいの?」
ことり「outってキーワードを使うの」

ことり「こうすると、Tは出力にしか使われません、ってコンパイラに教えてあげられるんだ」
凛「なるほど、今度はコンパイルできるにゃ」
ことり「これの逆で、入力にしか使われない型パラメータをマークするのに使うinもあります」


利用側変性:型投影

凛「トレース・・・」
ことり「それ、海未ちゃんがいるときにしよっか」

ことり「さっきの宣言側変性では、クラスとかインターフェイスの宣言時にoutinをつけたけど、これをインスタンスの利用時につけることもできます」
凛「利用時ってことは、同じジェネリッククラスのインスタンスでも、使う場所によって変性を変えられるってこと?」
ことり「うん。関数の引数で受け取る場合とかね。配列のコピーとかがわかりやすいと思うけど」

ことり「こんなArrayクラスがあったとしてね」

ことり「Arrayから別のArrayに中身をコピーする関数を作りました。一見、なんでもコピーできそうだよね」
凛「Array<Any>だから何渡しても動きそうにゃ」
ことり「ところが・・・」

ことり「これはコンパイルが通らないの。凛ちゃん、どうしてかわかる?」
凛「むむむ・・・えーと、さっきと同じでArray<Int>Array<Any>で型が違うからにゃ!」
ことり「正解。ということは、さっきみたいにoutでコンパイラに教えてあげないといけないよね」
凛「copyが受け取った引数fromは出力だけだからだね」
ことり「こういう場合は」

ことり「この引数fromは出力専用だよ、って教えてあげるのに、引数宣言のところでoutを使います」
凛「コンパイル通るようになったにゃ!」
ことり「この場合、引数fromは単なる配列ではなくて投影された配列であるといいます。これが型投影」


ジェネリック関数

ことり「クラスだけじゃなくて、関数をジェネリックにすることもできます」

ことり「funの後に<T>みたいに型パラメータを宣言してあげるだけ。拡張関数も書けます」


ジェネリック制約

ことり「これまでに見た型パラメータは、ほんとになんでも指定できたんだけど、そこに制限をかけたい場合があります。そういう場合は」

ことり「<T : Idol>みたいに書くと、Idolクラスのサブクラスしか指定できなくなります。これが上限境界」
凛「entry(Alpaca())でコンパイルエラー起こすね。ファイナルライブ出てたのに」
ことり「アルパカさんは、アイドルじゃないから・・・」

ことり「2つ以上の上限境界を設定したい場合は、whereを使って書きます」

ことり「この場合、SingableDanceableの2つのインターフェイスを実装したクラスしか型パラメータに指定できません」


凛「ただ使う分には難しくなさそうに見えて、意外と奥が深かったにゃ・・・」
ことり「Javaのジェネリクスの問題点を解消するために苦労した痕跡が見えるよね」

ことり「次回は一息つけるかな。列挙クラスを見てみます」


LINEで送る
Pocket


返信を残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です