yhara.jp

Recent Posts

変性、上界、下界について

Tech

ジェネリクス関連の概念、変性(variance)、上界(upper bound)、下界(lower bound)についての個人的なメモです。

参考文献

例について

クラスShapeがあり、そのサブクラスとしてクラスCircleやクラスRectがあるとする。Shapeの親クラスはObjectとし、ObjectのサブクラスとしてString等があるとする。

  • foo(x: Shape)という関数にはCircleやRectを渡せるがStringは渡せない
  • x: Shapeという変数にはCircleやRectを入れられるがStringは入れられない

変性(variance)

  • Array[Shape]Array[Circle]の関係は?
    • foo(items: Array[Circle])Array[Shape]を渡してもよいか?
      • →これはだめ
    • foo(items: Array[Shape])Array[Circle]を渡してもよいか?
      • →これは一見良さそうにも見えるが、foo内でitems.push(rect)とかしている可能性があるのでだめ
  • ということでScalaでは普通にやるとこれは許可されない(非変、invariant)
    • 逆にfoo内でitemsに書き込みしないなら渡してもよい。例えばImmutableArrayみたいなクラスを作ったとしたら?
  • まとめると、コンテナクラスGがあるとき、G[String]G[Object]は一般には互換ではない
    • これをG[String] <: G[Object]とするのが共変指定(G[+A])
    • 逆にG[Object] <: G[String]とするのが反変指定(G[-A])

共変(covariant)

  • (Scalaの場合) https://dwango.github.io/scala_text/type-parameter.html
  • class G[+A]のように+を付けると、G[Object]を受け取る関数にG[String]を渡すことが許可される
  • +を付けたのに書き換えとかしようとした場合はコンパイルエラーになる
class G[+T](var x: T) {
  def set(newX: T) { x = newX }
}

def foo(items: G[Object]) { }
val items = new G[String]("foo")
foo(items)

// (略)/a.scala:20: error: covariant type T occurs in contravariant position in type T of value x_=
// class G[+T](var x: T) {
//                 ^
// (略)/a.scala:21: error: covariant type T occurs in contravariant position in type T of value newX
//   def set(newX: T) { x = newX }

反変(contravariant)

共変の逆(G[Object] <: G[String])

Action<object> objAction = x => { Console.Write(x); };
Action<string> strAction = objAction;
  • 共変:「このコンテナは書き換えを行わないので大丈夫です」
  • 反変: 「このコンテナに入れたもは取り出さないので大丈夫です」

declaration-site vs. use-site variance

name decl-site use-site
Java no yes
Scala yes no
C# yes yes
Kotlin yes yes
Swift no no

境界(bounds)

上界(upper bound)

  • T <: Aのとき、T型の値に対してAのメソッドを呼べる

下界(lower bound)

  • Javaとかにはない
  • 共変と組み合わせて使う https://dwango.github.io/scala_text/type-parameter.html
    • 「このコンテナは書き換えを行うが、最初に入れたものと同じクラス(かスーパークラス)のオブジェクトしか入れないので大丈夫です」という指定
    • cf. 共変のみを指定した場合は「このコンテナは書き換えを行わないので大丈夫です」という指定になる

メモ: Scalaの変性チェック

共変に指定したとき、どこまでが許されるのか。

Scalaは結構厳しくて(v2.12.2)、外部からTを受け取ることをそもそも許さないようになっていた。immutableなコンテナならTを受け取らなくてもいいよね?ってことか。

class G[+T]() {
  def foo(newX: T) {  }
}
//prog.scala:2: error: covariant type T occurs in contravariant position in type T of value newX
//  def foo(newX: T) {  }

More posts

Posts

(more...)

Articles

(more...)

Category

Ads

About

About the author