LoginSignup
1
1

More than 5 years have passed since last update.

がばがばscala独学 - 型パラメータ(1)

Last updated at Posted at 2017-08-24

はじめに

下記のテキストを通して、基本的な書き方を学びつつ、個人的に気になったことを検証していくメモ代わりです。
https://dwango.github.io/scala_text/basic.html
本当にありがとう、dwango様・・・

型パラメータ

  • ざっくりまとめ
    • 型パラメータを使用したクラス定義
      • class className[T] など [] 内に型パラメータを記載
    • 継承なく、同一の型( A , B )に対して
      • 非変: A = B
    • AB を継承しているとき
      • 共変: B = A
      • 反変: A = B
        • 反変のメリットは不明・・・(要調査)

クラス定義の文法

  • 基本型

    class クラス名[型パラメータ1, 型パラメータ2, ..., 型パラメータN](コンストラクタ引数1 :コンストラクタ引数1の型, コンストラクタ引数2 :コンストラクタ引数2の型, ...) {
      0個以上のフィールドの定数またはメソッド定義
    }
    
  • 実際の使用例

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    class Cell[A](var value: A) {
      def put(newValue: A): Unit = {
        value = newValue
      }
      def get(): A = value
    }
    
    val cell = new Cell[Int](1)
    println(cell.get())
    cell.put(2)
    println(cell.get())
    
    // Exiting paste mode, now interpreting.
    
    1
    2
    defined class Cell
    cell: Cell[Int] = Cell@bf0b927
    
  • 型パラメータが効いて、エラーになるパターン

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    class Cell[A](var value: A) {
      def put(newValue: A): Unit = {
        value = newValue
      }
      def get(): A = value
    }
    
    val cell = new Cell[Int](1)
    cell.put("something")
    
    // Exiting paste mode, now interpreting.
    
    <console>:19: error: type mismatch;
     found   : String("something")
     required: Int
                  cell.put("something")
                           ^
    

実用的な例

  • 基本型に忠実に作成した場合

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    class Pair[A, B](val a:A, val b:B) {
    override def toString(): String = "(" + a + "," + b + ")"
    }
    
    def divideFull(m: Int, n:Int): Pair[Int, Int] = new Pair[Int, Int](m/n, m%n)
    divideFull(7, 3)
    
    // Exiting paste mode, now interpreting.
    
    defined class Pair
    divideFull: (m: Int, n: Int)Pair[Int,Int]
    res4: Pair[Int,Int] = (2,1)
    
  • 引数より推測できる場合

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    class Pair[A, B](val a:A, val b:B) {
    override def toString(): String = "(" + a + "," + b + ")"
    }
    
    def divide(m: Int, n:Int): Pair[Int, Int] = new Pair(m/n, m%n)
    divide(7, 3)
    
    // Exiting paste mode, now interpreting.
    
    defined class Pair
    divide: (m: Int, n: Int)Pair[Int,Int]
    res5: Pair[Int,Int] = (2,1)
    
  • ちなみに、型指定じゃなくした場合

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    class Pair[A, B](val a:A, val b:B) {
    override def toString(): String = "(" + a + "," + b + ")"
    }
    
    def divide(m: Int, n:Int): Pair = new Pair[Int, Int](m/n, m%n)
    divide(7, 3)
    
    // Exiting paste mode, now interpreting.
    
    <console>:13: error: class Pair takes type parameters
                def divide(m: Int, n:Int): Pair = new Pair[Int, Int](m/n, m%n)
                                           ^
    
    • 型定義の場合は推測してくれない

変位指定(variance)

非変(invariant)

  • 何も指定しなかった型パラメータは通常は非変(invariant)になる

    val : G[A] = G[B]
    
    • 上記ができる場合は、非変であり A=B である場合のみである
  • 実例

    • 代入できない場合

      scala> val arr: Array[Any] = new Array[String](1)
      <console>:7: error: type mismatch;
      found   : Array[String]
      required: Array[Any]
      Note: String <: Any, but class Array is invariant in type T.
      You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
           val arr: Array[Any] = new Array[String](1)
                                 ^
      
    • 代入できる場合

      scala> val arr: Array[String] = new Array[String](1)
      arr: Array[String] = Array(null)
      

共変(covariant)

  • A が B を継承しているときにのみ代入が許される性質

    val : G[B] = G[A]
    
  • 基本型

    class G[+A]
    
  • 実例

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    class Pair[+A, +B](val a: A, val b: B) {
      override def toString(): String = "(" + a + "," + b + ")"
    }
    
    val pair: Pair[AnyRef, AnyRef] = new Pair[String, String]("foo", "bar")
    
    // Exiting paste mode, now interpreting.
    
    defined class Pair
    pair: Pair[AnyRef,AnyRef] = (foo,bar)
    
    • Pairは作成時に値を与えたら後は変更できず、したがってArrayStoreExceptionのような例外が発生する余地がない
    • 一般的には、一度作成したら変更できない(immutable)などの型パラメータは共変にしても多くの場合問題がない

反変(contravariant)

  • A が B を継承しているときにのみ代入が許される性質

    val : G[A] = G[B]
    
  • 基本型

    class G[-A]
    
  • 実例

    scala> val x1: AnyRef => AnyRef = (x: String) => (x:AnyRef)
    <console>:7: error: type mismatch;
    found   : String => AnyRef
    required: AnyRef => AnyRef
         val x1: AnyRef => AnyRef = (x: String) => (x:AnyRef)
                                                ^
    
    scala> val x1: String => AnyRef = (x: AnyRef) => x
    x1: String => AnyRef = <function1>
    
    
  • 単純に逆にしてみるとどうなる?

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    class Pair[-A, -B](val a: A, val b: B) {
    override def toString(): String = "(" + a + "," + b + ")"
    }
    
    val pair: Pair[String, String] = new Pair[AnyRef, AnyRef]("foo", "bar")
    
    // Exiting paste mode, now interpreting.
    
    <console>:7: error: contravariant type A occurs in covariant position in type => A of value a
         class Pair[-A, -B](val a: A, val b: B) {
                                ^
    <console>:7: error: contravariant type B occurs in covariant position in type => B of value b
         class Pair[-A, -B](val a: A, val b: B) {
                                          ^
    
    • 反変の使い所がわからないなー。。。
      • コメント参照
1
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1