Scalaに、可変コレクションでvalを使用する場合と不変コレクションでvarを使用する場合のガイドラインはありますか?または、不変コレクションでvalを目指すべきですか?
両方の種類のコレクションがあるという事実は私に多くの選択を与えます、そして、私はしばしばその選択をする方法を知りません。
よくある質問、これ。難しいのは、重複を見つけることです。
参照の透明性に努力する必要があります。つまり、「e」という表現があれば、val x = e
、およびe
をx
に置き換えます。これは、可変性が壊れるプロパティです。設計を決定する必要があるときはいつでも、参照の透明性を最大化します。
実際問題として、メソッドローカルvar
は、メソッドをエスケープしないため、存在する最も安全なvar
です。メソッドが短い場合、さらに良い。そうでない場合は、他のメソッドを抽出して削減してみてください。
一方、可変コレクションにはpotentialがあります(エスケープしない場合でも)。コードを変更する場合、他のメソッドに渡すか、返すことができます。それは、参照の透明性を壊すようなものです。
オブジェクト(フィールド)でも、ほとんど同じことが起こりますが、より悲惨な結果になります。いずれにせよ、オブジェクトは状態を持つため、参照の透過性が失われます。しかし、変更可能なコレクションを持つことは、オブジェクト自体でさえ、誰がそれを変更しているかの制御を失う可能性があることを意味します。
不変のコレクションで作業し、それらを「変更」する必要がある場合、たとえばループで要素を追加する場合、結果のコレクションをどこかに保存する必要があるため、var
sを使用する必要があります。不変のコレクションからのみ読み取る場合は、val
sを使用します。
一般に、参照とオブジェクトを混同しないようにしてください。 val
sは不変の参照(Cの定数ポインター)です。つまり、val x = new MutableFoo()
を使用すると、オブジェクトを変更するがx
が指しますが、できなくなりますどのオブジェクトに変更するにはx
ポイント。 var x = new ImmutableFoo()
を使用する場合、逆のことが当てはまります。私の最初のアドバイスを取り上げます:参照が指すオブジェクトを変更する必要がない場合は、val
sを使用します。
これに答える最良の方法は、例を使用することです。何らかの理由で単純に数字を収集するプロセスがあるとします。これらの番号をログに記録し、コレクションをanotherプロセスに送信して、これを行います。
もちろん、コレクションをロガーに送信した後も、まだ数字を収集しています。そして、実際のロギングを遅らせるロギングプロセスにオーバーヘッドがあるとしましょう。うまくいけば、これがどこに向かっているかを見ることができます。
このコレクションを可変のval
(継続的に追加するために可変)に保存する場合、これは、ロギングを行うプロセスが同じオブジェクトを見ていることを意味します収集プロセスによって更新されます。そのコレクションはいつでも更新される可能性があるため、ログを記録する時間になったときに、実際に送信したコレクションを記録しているとは限りません。
不変のvar
を使用する場合、不変のデータ構造をロガーに送信します。コレクションにさらに番号を追加すると、置換var
が新しい不変のデータ構造になります。これは、ロガーに送信されたコレクションが置き換えられることを意味しません!送信されたコレクションを引き続き参照しています。したがって、ロガーは実際に受信したコレクションをログに記録します。
同時実行のシナリオでは、どのコンボを使用するかという問題がさらに重要になるため、このブログ投稿の例はさらに明らかになると思います: 並行性の不変性の重要性 そして、私たちがそれに取り組んでいる間に、同期vs @volatile対AtomicReferenceのようなものの好ましい使用に注意してください: three tools