第21章：Scalaの型パラメータ

皆、元気してた？久々の投稿をさせてもらうね。 
今回から __応用編__ に突入するよ！

今回のテーマは、 __型パラメータ__ だ！ 
こんな感じで語っていくからね。

- [型パラメータとは](#型パラメータとは)
- [型境界](#型境界)
- [変位指定アノテーション](#変位指定アノテーション)
- [まとめ](#まとめ)

# <a name="型パラメータとは">型パラメータとは</a>
型パラメータってなんだろ？ 意味分かんないぞ！ 
と思っていたら、Javaで言う __ジェネリックス(総称型)__ のことを、Scalaでは __型パラメータ__ と呼ぶみたいなんだ。

型自体をパラメータとし使うことができるんだ。

クラスの定義自体はジェネリックスにしておいて、インスタンス化するときに型を決定する。 
なんてことができるようになるんだ。

例えば、Javaの`ArrayList`の定義を抜粋するとこんな感じです。

```java
public class ArrayList<E> {...}
```

`<E>`でジェネリックスだぜって言っておいて 
実際に使うときは、

```java
final List<String> arrays = new ArrayList<String>()

```

として、`E`だったところに`String`として、`ArrayList`のインスタンスを生成します。 
これでこのListには、`String`型の要素しか入れれないぜ！てことができるんだ。

同じことを、もちろんScalaでもできるのです。


## <a name="型パラメータのメリット">型パラメータのメリット</a>

Scalaでの例を見る前に、ちょっとメリット的なものを。

僕は具体的な型パラメータを決めることを、 __型縛り__ と呼ぶんだけど 
型縛りできるおかげで

- 違う型を使うとコンパイルエラーになる
- キャストが必要なくなる

なんて恩恵を得られるようになるんだ。

例えばコレクション系のMapやSeqで型縛りすれば、 __縛った型__ の要素だけを入れるようにすることができる。

あと抽象度が高いモノが作れるんだ。

さっき見たJavaの`ArrayList`がまさにそうで、`ArrayList`自身は何かの要素を入れれる箱として定義している。 
箱に何を入れるかはインスタンス化する時に決めれる。


## <a name="型パラメータを使ってみる">型パラメータを使ってみる</a>

では、Scalaでの型パラメータを使ってみよう！

こんなの作ってみたぜ！

```scala
object TypeParamSample {

  class TypeParam[T](val t: T) {
    def get: T = this.t
  }

  def main(args: Array[String]): Unit = {
    val stringTypeParam = new TypeParam[String]("test")
    println(stringTypeParam.get)

    val intTypeParam = new TypeParam[Int](1)
    println(intTypeParam.get)
  }

}
```

説明するぞ

### 型パラメータのクラスの定義

```scala
  class TypeParam[T](val t: T) {
    def get: T = this.t
  }
```

`class TypeParam[T]`で型パラメータを使うクラスを宣言したぞ。 
Javaだと`<...>`なんだけど、Scalaだと`[...]`になるんだ。

何が`T`なんだ？ということで、インスタンス変数`t`の型を型パラメータで解決することにする。 

`def get: T = this.t`は`t`の値を返すメソッドです。


### 使う側

使う側はシンプルだよ！

```scala
  def main(args: Array[String]): Unit = {
    val stringTypeParam = new TypeParam[String]("test")
    println(stringTypeParam.get)

    val intTypeParam = new TypeParam[Int](1)
    println(intTypeParam.get)
  }
```

ここでは`String`と`Int`の2つの型で、`TypeParam`を使ってみました。

### 動かす

```
$ scalac TypeParamSample.scala
$ scala TypeParamSample
test
1
```

ちゃんとgetできているね！



# <a name="型境界">型境界</a>

次に __型境界__ だ！

型境界とは型の範囲を決めることです。 
ある程度ゆるい範囲を型パラメータとして使えるようになるのがメリットかな。

パラメータ化した型の範囲を定義します。 
Scalaの型は、`Any`を頂点として継承の階層構造となっているので、範囲を決めることができるのです。

Javaの`<? extends A>`と似ていますね。

- __上限境界__
- __下限境界__

があります。 


実際に見て行く前に、以下の型を定義しておきます。

```scala
scala> class Animal
defined class Animal

scala> class Person extends Animal
defined class Person

scala> class Student extends Person
defined class Student
```


## 上限境界
スーパー型の上限を決めるのです。

`A <: B`で、以下のどちらかの制約になるんだ。

- `A`は`B`のサブ型
- `A`は`B`と同一型

上限境界を定義した新しいクラスを定義してみる。

```scala
scala> class School[A <: Person]
defined class School
```

型パラメータを与えてインスタンスを作ってみよう！

### サブ型の場合

```scala
scala> new School[Student]
res0: School[Student] = School@7de3ec13
```

問題なしですね。

### 同一型の場合

```scala
scala> new School[Person]
res1: School[Person] = School@35203079
```

これも問題なし。

### 境界外の型の場合

```scala
scala> new School[Animal]
<console>:10: error: type arguments [Animal] do not conform to class School's type parameter bounds [A <: Person]
       val res2 =
           ^
<console>:11: error: type arguments [Animal] do not conform to class School's type parameter bounds [A <: Person]
              new School[Animal]
                  ^

```

`Animal`は __上限境界__ を超えているので、エラーになりました！


## 下限境界
サブ型の下限を決めるのです。

まぁ上限の逆だよね。

`A >: B`で、以下のどちらかの制約になるんだ。

- `A`は`B`のスーパ型
- `A`は`B`と同一型

今度は、下限境界を定義した新しいクラスを定義してみる。


このクラス達を使ってみてみよう！

```scala
scala> class School[A >: Person]
defined class School
```

下限境界でも、型パラメータを与えてインスタンスを作ってみよう！

### スーパー型の場合

```scala
scala> new School[Animal]
res0: School[Animal] = School@4d832421
```

問題なしですね。

### 同一型の場合

```scala
scala> new School[Person]
res1: School[Person] = School@4d032cfc
```

これも問題なし。

### 境界外の型の場合

```scala
scala> new School[Student]
<console>:11: error: type arguments [Student] do not conform to class School's type parameter bounds [A >: Person]
       val res2 =
           ^
<console>:12: error: type arguments [Student] do not conform to class School's type parameter bounds [A >: Person]
              new School[Student]
                  ^
```

`Student`は __下限境界__ を超えているので、エラーになりました！


## 上限・下限境界

上限と下限を同時に設定することもできるんだ。

```scala
scala> class School[A >: Student <: Animal]
defined class School
```

これは`Animal` `Person` `Student`をOKにしたんだ。

# <a name="変位指定アノテーション">変位指定アノテーション</a>

これはJavaの世界ではない考え方です。

Javaの場合、`String`が`Object`のサブクラスだからといって、`List<String>`が`List<Object>`のサブクラスとは言えないですよね。
つまり

```java
final List<String> list = new ArrayList<String>();
final List<Object> objList = list;
```

とすると、2行目がコンパイルエラーになります。

でもScalaではこれが可能となります！ 
そして、それを可能とするのが変位指定アノテーションです。

Scaladocを覗いてみます。ここでは`List`クラスを取り上げます。

```scala
sealed abstract class List[+A] extends AbstractSeq[A] with LinearSeq[A] with Product with GenericTraversableTemplate[A, List] with LinearSeqOptimized[A, List[A]]
```

ちょっと例としてはかなり複雑ではあるですけどね。。。 
なのでちょっと抜粋します。

```scala
class List[+A]
```

これでわかりやすくなったでしょ。削り過ぎとかは言わないでね！

この`List`クラスは型パラメータです。 
そして型パラメータの`+A`となっています。 
この中の`+`が、 __変位指定アノテーションの世界__ となります。
実は、`+`なしの場合も、立派に変位指定アノテーションなのです。

この変位指定アノテーションは、型の許容性を明示します。 
__型の方向性__ と考えるとわかりやすいかも。 

- 汎化の方向性で許容するのか
- 特化の方向性で許容するか 

更に方向性を持たない場合の3つがあります。用語があるので纏めると

- 共変
    - 型の特化を許容する。型パラメータで`+A`とする。
- 反変
    - 型の汎化を許容する。型パラメータで`-A`とする。
- 非変
    - その型のみを許容する。型パラメータで`A`とする。

それぞれ見てみましょう。 
ここでは、型境界で登場した`Animal`、`Person`、`Student`クラスに再登場していもらいます。

## 共変

- 型の特化を許容する

サブクラスを許容します。制限を付けたいときは、下限境界が使えるぞ。

ソースを見てみよう！

```scala
scala> class School[+T]
defined class School

scala> def receive(args: School[Person]) = println("success!!!")
receive: (args: School[Person])Unit
```

ここでは共変な`School`クラスを定義しています。 
そして`School[Person]`を受け取れる関数`receive`を定義した。

ではこの関数を使ってみよう！

```scala
scala> receive(new School[Person])
success!!!
```

`receive`の引数の型そのモノを渡しているのでOKですね。

```scala
scala> receive(new School[Student])
success!!!
```

共変なため、`School[Person]`のサブクラスの`School[Student]`もOKです。

```scala
scala> receive(new School[Animal])
<console>:12: error: type mismatch;
 found   : School[Animal]
 required: School[Person]
              receive(new School[Animal])
                      ^
```

`School[Person]`のスーパークラスの`School[Animal]`の場合は、コンパイルエラーになりました。

## 反変

- 型の汎化を許容する

スーパクラスを許容します。制限を付けたいときは、上限境界が使えるぞ。

同様に見てみます。

```scala
scala> class School[-T]
defined class School

scala> def receive(args: School[Person]) = println("success!!!")
receive: (args: School[Person])Unit

scala> receive(new School[Person])
success!!!

scala> receive(new School[Animal])
success!!!

scala> receive(new School[Student])
<console>:13: error: type mismatch;
 found   : School[Student]
 required: School[Person]
              receive(new School[Student])
                      ^
```

今度は`School[Student]`の場合に、コンパイルエラーが発生しました。

## 非変

- その型のみを許容する

こちらも同様に。

```scala
scala> class School[T]
defined class School

scala> def receive(args: School[Person]) = println("success!!!")
receive: (args: School[Person])Unit

scala> receive(new School[Person])
success!!!

scala> receive(new School[Animal])
<console>:12: error: type mismatch;
 found   : School[Animal]
 required: School[Person]
Note: Animal >: Person, but class School is invariant in type T.
You may wish to define T as -T instead. (SLS 4.5)
              receive(new School[Animal])
                      ^

scala> receive(new School[Student])
<console>:13: error: type mismatch;
 found   : School[Student]
 required: School[Person]
Note: Student <: Person, but class School is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
              receive(new School[Student])
                      ^
```

サブクラス・スーパークラスのどちらも許容しないことが確認できました。

ちなみに __Javaの世界__では、 __全て非変__ となっています。 
ただし、 __配列は共変__ です。これは知らなかったな。。。

# <a name="まとめ">まとめ</a>
今回は書き方もちょっと変えてみたぞ！内部リンクを付けてみたけどどうかな？必要ないって？

型の世界は奥が深いだろ。 
でもこの辺がわかってくると、Scaladocを今までよりももっと読めるようになるよ！

ところで応用編に突入してみたけど、今までのノリと変わってないだろ？

今回も
__体で感じてくれたかな？__