2011-07-13 11 views
43

など。 (配列は不変であるため)なぜ配列は不変ですが、リストは共変型ですか?

val list:List[Any] = List[Int](1,2,3) 

仕事が、

val arr:Array[Any] = Array[Int](1,2,3) 

失敗しない理由。この意思決定の背後にある希望の効果は何ですか?

+7

Java配列は共変なので、scalaからJavaコードを呼び出すときに問題が生じる可能性があることに注意してください。 – incrop

+0

@incrop - 例を挙げていただけますか? –

答えて

64

それ以外の場合は型安全性が損なわれるためです。

val arr:Array[Int] = Array[Int](1,2,3) 
val arr2:Array[Any] = arr 
arr2(0) = 2.54 

をし、コンパイラはそれをキャッチカント: ない場合は、このような何かをすることができるだろう。

あなたは違いないInt

+1

'List'ではなく' Array'を意味しますか?リストでは、あなたの例は動作しません( 'List'型の" update "メソッドはありません)。 'Array'sでは、あなたができることの有効な反例になります。配列は共変です。 – Dirk

+0

はい、申し訳ありません。私はそれを更新しました –

+1

私はちょうど例を入れ、彼が言った言い分を改めるので、クレジットは@ sshanninに行くべきです。 –

28

リストが不変であり、配列が変更可能であるためです。

+3

私はちょうど例を入れて、彼が言ったことを言い換えると、信用は@sshanninに行くべきです。 –

+12

-1。なぜ人々はこれのような答えをupvoteするのですか?なぜこれが関連しているのかについての説明がなければ、「配列」は「A」で始まり、「リスト」は「L」で始まるからです。 –

+1

それは良い点だ、トラビス。私はあなたのコメントを読む前に私はそれをupvotedし、私はここに来たときに私はすでに答えを知っていたと私はそれを簡潔に明示したので、私はそれをupvoted気づいた。しかし、あなたが既に答えを知っていればうまくいく答えはそれほど有用ではありません。 – csjacobs24

4

ある何かを追加することはできませんので、一方、リストは、不変ではArray sが可変している間List sが不変であるということです。

可変性が分散を決定する理由を理解するには、Listの可変バージョンを作成することを検討してください。MutableListとしましょう。また、基本クラスAnimalCatDogの2つのサブクラスを使用します。

trait Animal { 
    def makeSound: String 
} 

class Cat extends Animal { 
    def makeSound = "meow" 
    def jump = // ... 
} 

class Dog extends Animal { 
    def makeSound = "bark" 
} 

CatDogより1つの方法(jump)を有していることに注意してください。その後

、動物の変更可能なリストを受け取り、リストを変更する機能を定義します。あなたが関数に猫のリストを渡す場合

def mindlessFunc(xs: MutableList[Animal]) = { 
    xs += new Dog() 
} 

今、恐ろしいことが起こります:

val cats = MutableList[Cat](cat1, cat2) 
val horror = mindlessFunc(cats) 

不注意なプログラミング言語を使用していた場合、これはコンパイル時に無視されます。それにもかかわらず、我々は唯一の次のコードを使用して猫のリストにアクセスする場合、私たちの世界は崩壊しません。

cats.foreach(c => c.makeSound) 

をしかし、我々はこれを行う場合:

cats.foreach(c => c.jump) 

実行時エラーが発生します。 Scalaでは、コンパイラが不平を言うので、そのようなコードを書くことはできません。

+0

これは疑問に答えるものではなく、実際には配列については言及していません。 OPは配列の可変性から問題が生じると推測するかもしれないが、ここでは述べていない。 – csjacobs24

+1

@ csjacobs24質問の直接回答を得るために、回答の先頭に行を追加しました。元の答えの目的は、変更可能なリストが共変する理由が間違っている理由を説明することでした。 –

+0

@YuhuanJiang良い答えは、リストが不変(デフォルト)のときにどのように問題が起こらないかを説明するために、それを拡張する方が良いかもしれません。また、答えの冒頭では、liskovの原則と一緒に知らない人のために実際にどのような共分散があるのか​​を説明することから始めることができます。この重要な質問に対するここの答えはどれも、包括的ではありません。 – Rpant

4

通常の答えは、共分散と組み合わされた可変性が型の安全性を破壊するということです。コレクションについては、これは基本的な真理として考えることができます。しかし、この理論は実際にはListArrayのようなコレクションだけでなく、一般的な型にも当てはまります。

本当の答えは、関数型がサブタイプ化とやり取りする方法と関係しています。簡単な話は、型パラメーターが戻り値の型として使用されている場合、それは共変です。一方、型パラメータが引数型として使用されている場合、それは反変です。それが戻り型と引数型の両方として使用される場合、それは不変です。

documentation for Array[T]を見てみましょう。検索や更新のためのもののためのものです探しするには、2つの明白な方法:

def apply(i: Int): T 
def update(i: Int, x: T): Unit 

第一の方法T、第2 Tに引数の型である一方で、戻り値の型です。分散のルールは、したがって、Tは不変でなければならないと指示する。

documentation for List[A]を比較して、なぜそれが共変であるかを見ることができます。紛らわしいことに、我々はArray[T]ための方法に類似しているこれらのメソッドを、見つけるだろう:

def apply(n: Int): A 
def ::(x: A): List[A] 

Aは、戻り値の型の両方として、引数の型として使用されているので、我々はTがあるだけのようAが不変であることを期待しますArray[T]しかし、Array[T]とは異なり、ドキュメントは::のタイプについて私たちに嘘をついています。このメソッドへのほとんどの呼び出しではうそは十分ですが、Aの分散を決定するには十分ではありません。

def ::[B >: A](x: B): List[B] 

だからAが実際に引数の型として表示されません。私たちは、このメソッドのドキュメントを拡張し、「フル署名」をクリックすると、私たちは真実を示しています。代わりに、B(いずれのスーパータイプもAとすることができます)が引数の型です。これはAに何らの制限も設けていないので、実際には共変型である可能性があります。引数の型としてAを持つList[A]上のメソッドは、同様の嘘です(これらのメソッドは[use case]とマークされているのでわかります)。

関連する問題