19.0511
ジェネリクス / Generics
ジェネリクス とは、
すべてのオブジェクト / Any に、対応した、class を、作る/使う、技術/わざ/工夫/アイデア
です。
でも、まあ、これになれて普通に使っていれば、「もともとある使い方」 のように感じられて、
今の説明に、ちょっと大げさ感 を、感じられるでしょう。
でも、ジェネリクス を使う事で、
データ/型 の不一致 を、コンパイラ が、見つけてくれる/保証してくれる
という、メリットがあります。
「14. List」 のところでも、
val stringList: List<String> = listOf("apple", "strawberry")
// println(stringList) ← println(Listインスタンス)
のようなコードで、ジェネリクス を、使いました。
1. ジェネリクス を使う
使い方は、簡単で、
Stringインスタンス の、集まりで、Listインスタンス を、作りたければ、
val stringList: List<String> = listOf("apple", "strawberry")
で、OK です。
つまり、データ/値 の、型/type を、特定する方法です。
2. ジェネリクス を、使って class を作る
前置きとして、私は、ジェネリクス を、応用して、「本格的なclass」 までは、作れません。
あくまで、しくみ に、ついて見ていくだけですので、ご了承ください。
ただ、これを見ておくと、ジェネリクス への、理解が深まると思います。
まず、こんなclass を、見てください。
このコード を、上から順にみていきましょう。
その前に、このコード は、特に目的はないのですが、無理矢理、目的を書くと、
3行目の、変数anything(なんでも という意味)に、対して、
class を、インスタンス化 する時に、どんな型でも指定できる/何でも指定できる
ような、class を、目指して作っています。
では、見ていきましょう。
1行目:
これは、IntelliJ先生に、「✓」 を、付けていただくために、
constructor()
は、省略しようよ、、という教えに従ったため、コメントにしました。
私を含め、Kotlin初心者には、省略しない方が、「やさしいコード」 なのですが、
「✓」 が、付いていれば、
論理エラー
// 文法的には、合っているが、そもそもやっていることが、
// 意図に反して間違ったコードを書いている場合.
は、どうしようもないですが、とりあえず、安心感がありますね。
2行目:
1行目を、省略して書きました。
3行目:
変数anything に対して、
T? に、ジェネリクス(Generics(総称型))、そして、
プラス、null許容型(27. null許容型)に、しています。
/ 作法的に、いいのか悪いのかは、不明。
/ こうしておけば、anything を、null で、初期化できます。
/ 「全く何もない null」で、初期化というのも、変な話ではありますが。
/ コンピュータの世界では、「何もない値」 も、ありのようです。
4行目:
これは、ジェネリクス を、使わず、
anything の、説明のための、Stringインスタンス/文字列 の 変数、
whatIsAnyQ(anythingとは何ですか?)を、"null"という文字列で初期化しています。
5~7行目:
メソッド、showAnything()。
anything について、説明するメソッド。
10~26行目:
main()関数
14行目:
TestGenerics<T>クラス を、Any型 に指定して、インスタンス化 しています。
12行目のコメントが、省略しない書き方です。
val testG0: TestGenerics<Any> = TestGenerics<Any>()
ジェネリクス も、型推量できる場面では、省略できます。
val testG0: TestGenerics<Any> = TestGenerics()
15行目:
TestGenerics<Any>クラス の、インスタンス testG0 で、
showAnything()メソッド を、呼んでいます。
その結果は、コンソール画面1行目の、
「null」は、[null」。
です(println() では、文字列の「"」 は、表示しない)。
17行目:
今度は、TestGenerics<T>クラス を、Int型 に指定して、インスタンス化 しています。
val testG1: TestGenerics<Int> = TestGenerics()
18~20行目:
インスタンスtestG1 を使って、フィールド/プロパティ を、
設定して、メソッドshowAnything() を、呼んでいます。
結果は、コンソール画面2行目の、
「9」は、「nine」。
です。
22~25行目:
今度は、ジェネリクス<T>を、List<Double> に、指定して、インスタンス化 しています。
/ testG2
(List<Double>クラス 自体も、ジェネリクス を使ったクラス です)
ここでも、フィールド を、設定して、メソッド を、呼んでいます。
3. ジェネリクス の何が、便利/効率的 なのか
なにが、効率的か、これは、ジェネリクス を、使わなかった場合を、考えれば分かるでしょう。
今のコードで、使わなかった場合、
TestGenerics<T>クラス だけで済まず、例えば、
・TestNullクラス
・TestIntクラス
・TestListDoubleクラス
と、3つもクラス を、書かなければならなくなります。
しかも、、、もっと、他の 型/type に、たいして、同様の class を作るとしたら、、、
これは、List<T>クラス を、考えてみても分かると思いますが、
もう、ジェネリクス を、使うようになった人たちには、やってられないでしょう。
参考までに、
今のコード を、
を、ジェネリクス を、使わずに、書き換えてみました。
class が、3つも必要になりました。
まだ他に、違った型 を、使いたければ、もっと、class が、必要になります。
これを、便利と言わずして、、ですね。
まとめ
・ いつも何気なく使っている ジェネリクス は、実は、便利な「わざ」でした。
・ 覚えておきたい型の名前 の、代用(仮引数 ならぬ、仮タイプ) に関する 慣習(ルールではない):
T: 型/type を、表す.
E: 要素/element を、表す.
R: 返し値/return を、表す.
U: 複数ある時