2013-09-01 9 views
5

私はScalaのコレクションで遊んでいて、可変コレクションは不変として定義され、不変なコレクションは共変として定義されていることがわかりました。 Scalaでの分散と不変性/不変性の関係は何ですか?Scalaの分散と不変性/不変性との関係

class Array[T] 

class List[+T] 
+0

これはまさにその関係です。不変は共変、可変は不変です。 –

+0

事実、可変構造は、それらが共変するようにすると「困ってしまう」ということです。そのため、Scalaでは、共変タイプのパラメータを配置できる位置が制限されています。ここをクリックしてください:http://www.artima.com/pins1ed/type-parameterization.html 可能であれば、本の第2版で同じ章を見つけようとしてください。 – Felix

答えて

4

タイプAのタイプは、そのタイプのパラメータが共変動位置にのみ現れる場合にのみ、共変動性を示すことができる。一般に、これは、クラス/特性/オブジェクトがバリアント型の値を返すが、バリアント型のパラメータを持つメソッドを持たないメソッドを有することを意味する。変更可能なコレクションには、常にバリアント型のパラメータを持つメソッドがあります。 update

val as = Array[String]("a string") 

// this statement won't typecheck in actual Scala 
val aa: Array[AnyRef] = as 

aa(0) = ("I'm a string...", "but this tuple itself isn't!") 

// Tuples don't have a substring method, so this would fail at run-time 
// if the compiler allowed this code to compile. 
as(0).substring(0) 
5

私はSIAでの簡単な説明を発見した:Arrayはアレイ[+ T]宣言することができた場合に何が起こるか想像してみてください。次はそこからまっすぐです。

可変オブジェクトは不変である必要があります 型パラメータは共変も反変もない場合は不変です。すべてのScala可変コレクションクラスは不変です。例は、可変オブジェクトが不変である必要がある理由を説明することができます。それは不変として宣言されます

final class ListBuffer[A] ...{ ... } 

ので、あなたは別のタイプからListBufferを割り当てることはできません。ListBufferが可変であるので、以下のように、それは不変として宣言されます。次のコードはコンパイルエラーがスローされます。

scala> val mxs: ListBuffer[String] = ListBuffer("pants") 
mxs: scala.collection.mutable.ListBuffer[String] = 
      ListBuffer(pants) 
scala> val everything: ListBuffer[Any] = mxs 
<console>:6: error: type mismatch; 
found : scala.collection.mutable.ListBuffer[String] 
required: scala.collection.mutable.ListBuffer[Any] 
    val everything: ListBuffer[Any] = mxs 

を文字列scala.Anyのサブタイプであるにもかかわらず、ScalaはまだあなたがすべてにMXSを割り当てることはできません。理由を理解するには、ListBufferは共変で、次のコードスニペットは、任意のコンパイルの問題もなく動作しますと仮定します。

scala> val mxs: ListBuffer[String] = ListBuffer("pants") 
    mxs: scala.collection.mutable.ListBuffer[String] = 
     ListBuffer(pants) 
    scala> val everything: ListBuffer[Any] = mxs 
    scala> everything += 1 
    res4: everything.type = ListBuffer(1, pants) 

あなたは問題を見つけることができますか?すべてがAny型であるため、文字列のコレクションに整数値を格納できます。これは起こるのを待っている災害です。これはまさにJavaの配列に起こることです。この種の問題を回避するには、可変オブジェクトを不変にすることは常に良い考えです。次の質問は、コレクションの不変オブジェクトの場合に起こることです。不変オブジェクトの場合、共分散はまったく問題ではないことが分かります。 ListBufferを不変のListに置き換えた場合、List [String]のインスタンスを取得し、問題なくList [Any]に割り当てることができます。一覧は不変であるので、この割り当てが安全である

scala> val xs: List[String] = List("pants") 
xs: List[String] = List(pants) 
scala> val everything: List[Any] = xs 
everything: List[Any] = List(pants) 

唯一の理由です。 xs Listに1を加えれば、Any型の新しいListが返されます。

scala> 1 :: xs 
res5: List[Any] = List(1, pants) 

両論(ので繰り返しますが、このほかは安全である::)メソッドは常に新しいリストを返し、そのタイプはリスト内の要素の種類によって決定されます。整数値と参照値を格納できる唯一の型はscala.Anyです。これは、可変/不変オブジェクトを扱うときの型の分散について覚えておく重要な特性です。

contravarianceを理解する最良の方法は、不在時の問題を表示することです。次のJavaコード例で問題を発見してください。

Object[] arr = new int[1]; 
arr[0] = "Hello, there!"; 

文字列を整数配列に割り当てることになります。 Javaは実行時にArrayStoreExceptionをスローすることでこのエラーをキャッチします。スカラは、パラメータ型が反変または不変のいずれかになるように強制することによって、この種のエラーをコンパイル時に停止します。

これが役に立ちます。

+0

これは非常に良い答えです。 –