変更可能なコレクションで val を使用する場合と不変のコレクションで var を使用する場合について、Scala にガイドラインはありますか? それとも、イミュータブル コレクションで本当に val を狙うべきでしょうか?
両方のタイプのコレクションがあるという事実は、多くの選択肢を与えてくれますが、多くの場合、その選択方法がわかりません。
変更可能なコレクションで val を使用する場合と不変のコレクションで var を使用する場合について、Scala にガイドラインはありますか? それとも、イミュータブル コレクションで本当に val を狙うべきでしょうか?
両方のタイプのコレクションがあるという事実は、多くの選択肢を与えてくれますが、多くの場合、その選択方法がわかりません。
かなり一般的な質問、これ。難しいのは重複を見つけることです。
参照の透過性に努める必要があります。つまり、「e」という式があれば、 を作成してにval x = e
置き換えることができるということです。これは、可変性が壊れるプロパティです。設計上の決定を下す必要があるときはいつでも、参照の透過性を最大化します。e
x
実際問題として、メソッド ローカルは、メソッドをエスケープしないため、存在する中でvar
最も安全です。var
メソッドが短い場合は、さらに優れています。そうでない場合は、他の方法を抽出して削減してみてください。
一方、変更可能なコレクションは、たとえそうでなくても、エスケープする可能性があります。コードを変更するときは、それを他のメソッドに渡したり、戻したりしたい場合があります。それは、参照の透過性を壊すようなものです。
オブジェクト (フィールド) でも、ほぼ同じことが起こりますが、より悲惨な結果になります。いずれにせよ、オブジェクトには状態があるため、参照透過性が失われます。しかし、変更可能なコレクションを持つということは、オブジェクト自体でさえ、誰がそれを変更しているかを制御できなくなる可能性があることを意味します。
不変コレクションを使用していて、それらを「変更」する必要がある場合、たとえばループ内で要素を追加する場合はvar
、結果のコレクションをどこかに保存する必要があるため、s を使用する必要があります。不変コレクションからのみ読み取る場合は、val
s を使用します。
一般に、参照とオブジェクトを混同しないように注意してください。val
s は不変参照 (C の定数ポインター) です。つまり、 を使用すると、指しているオブジェクトを変更val x = new MutableFoo()
できますが、どのオブジェクトを指しているかを変更することはできません。を使う場合はその逆です。私の最初のアドバイスを取り上げます。参照先のオブジェクトを変更する必要がない場合は、s を使用してください。x
x
var x = new ImmutableFoo()
val
これに答える最良の方法は、例を使用することです。何らかの理由で単純に数値を収集するプロセスがあるとします。これらの数値をログに記録したいので、コレクションを別のプロセスに送信してこれを行います。
もちろん、コレクションをロガーに送信した後も数値を収集しています。そして、実際のロギングを遅らせるロギング プロセスのオーバーヘッドがあるとします。うまくいけば、これがどこに向かっているのかがわかります。
このコレクションを可変val
(継続的に追加しているため可変) に保存する場合、これは、ログを実行するプロセスが、コレクション プロセスによってまだ更新されている同じオブジェクトを参照することを意味します。そのコレクションはいつでも更新される可能性があるため、ログに記録するときに、送信したコレクションを実際にログに記録していない可能性があります。
immutable を使用する場合var
、不変のデータ構造をロガーに送信します。コレクションにさらに数値を追加すると、新しい不変データ構造に置き換えられます。これは、ロガーに送信されたコレクションが置き換えられるという意味ではありません! 送信されたコレクションをまだ参照しています。したがって、ロガーは実際に受信したコレクションをログに記録します。var
このブログ投稿の例は、同時実行シナリオでどのコンボを使用するかという問題がさらに重要になるため、より明確になると思います: 同時実行の不変性の重要性。そして、私たちがそれに取り組んでいる間、同期対 @volatile 対 AtomicReference のようなものの優先使用に注意してください: 3 つのツール
var immutable
対。val mutable
この質問に対する多くの優れた回答に加えて。の潜在的な危険性を示す簡単な例を次に示しval mutable
ます。
変更可能なオブジェクトは、メソッド内で変更でき、それらをパラメーターとして受け取りますが、再割り当ては許可されていません。
import scala.collection.mutable.ArrayBuffer
object MyObject {
def main(args: Array[String]) {
val a = ArrayBuffer(1,2,3,4)
silly(a)
println(a) // a has been modified here
}
def silly(a: ArrayBuffer[Int]): Unit = {
a += 10
println(s"length: ${a.length}")
}
}
結果:
length: 5
ArrayBuffer(1, 2, 3, 4, 10)
var immutable
再割り当てが許可されていないため、 では次のようなことは起こりません。
object MyObject {
def main(args: Array[String]) {
var v = Vector(1,2,3,4)
silly(v)
println(v)
}
def silly(v: Vector[Int]): Unit = {
v = v :+ 10 // This line is not valid
println(s"length of v: ${v.length}")
}
}
結果:
error: reassignment to val
val
関数パラメータはこの再割り当てが許可されていないものとして扱われるためです。