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: 複数ある時