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
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy