22

私は、Joshua Bloch 著『 Effective Java』の Item 15 を読んでいました。「可変性の最小化」について述べている項目 15 の中で、彼はオブジェクトを不変にするための 5 つのルールについて言及しています。それらの 1 つは、すべてのフィールドを final にすることです。ルールは次のとおりです。

Make all fields final : これにより、システムによって強制される方法で意図が明確に表現されます。また、新しく作成されたインスタンスへの参照が、メモリ モデル [JLS, 17.5; Goetz06 16]。

String クラスが不変クラスの例であることは知っています。ソース コードを調べてみると、実際には final ではないハッシュ インスタンスがあることがわかります。

//Cache the hash code for the string
private int hash; // Default to 0

では、どのように String が不変になるのでしょうか?

4

5 に答える 5

27

発言は、これが最終的ではない理由を説明しています。

//文字列のハッシュコードをキャッシュします

それはキャッシュです。を呼び出さないとhashCode、その値は設定されません。文字列の作成中に設定された可能性がありますが、不要な機能(ハッシュコード)の場合、作成時間が長くなることを意味します。一方、要求されるたびにハッシュを計算し、文字列が不変であるとすると、ハッシュコードが変更されることはありません。

非最終フィールドがあるという事実は、引用するその定義と多少矛盾しますが、ここでは、それはオブジェクトのインターフェースの一部ではありません。これは単なる内部実装の詳細であり、文字列の可変性(文字コンテナとして)には影響しません。

編集-人気のある需要のため、私の答えを完成させます:パブリックインターフェイスの直接の一部ではありませんが、その値を返すため、そのhashインターフェイスの動作に影響を与えた可能性があります。hashCode現在、hashCode同期されていないためhash、複数のスレッドがそのメソッドを同時に使用した場合、複数回設定される可能性があります。ただし、に設定される値は常に安定した計算の結果であり、最終フィールド( 、、および)hashのみに依存します。したがって、ハッシュを計算するたびに、まったく同じ結果が得られます。外部ユーザーの場合、これは1回計算されたかのように、そして毎回計算されたかのように、valueoffsetcounthashhashCode指定された値に対して一貫して同じ結果を返す必要があります。結論hashとして、最終的なものではありませんが、その可変性は外部のビューアには表示されないため、クラスは不変であると見なすことができます。

于 2012-07-01T13:16:35.613 に答える
9

Stringユーザーに関する限り、変更することはできず、すべてのスレッドで常に同じように見えるため、は不変です。

hashCode()hashCode()は際どいシングルチェックイディオム(EJ項目71)を使用して計算され、誤って複数回計算されても誰も傷つけないので安全です。

すべてのフィールドをfinalにすることは、クラスを不変にする最も簡単で簡単な方法ですが、厳密には必須ではありません。どのスレッドがいつ呼び出しても、すべてのメソッドが同じものを返す限り、クラスは不変です。

于 2012-07-01T13:15:59.823 に答える
1

論理的に不変であるクラスが同じ観察可能な状態に対していくつかの異なる表現を持ち、クラスのインスタンスがそれらの間で切り替えることができると役立つ状況がたくさんあります。ハッシュ フィールドがゼロの文字列から返されるハッシュコード値は、ハッシュ フィールドが以前のハッシュコード呼び出しの結果を保持している場合に返される値と同じになります。したがって、ハッシュ値を前者から後者に変更しても、オブジェクトの監視可能な状態は変更されませんが、将来の操作がより高速に実行されるようになります。

これらの方法で物事をコーディングする際の最大の困難は、

  1. オブジェクトが特定の不変オブジェクトへの参照を保持することから、同じ意味内容を持つ別のオブジェクトへの参照を保持するように変更された場合、そのような変更は、参照を保持するオブジェクトの監視可能な状態に影響を与えるべきではありませんが、特に、参照を保持していると思われるオブジェクトが他の意味的に同一のオブジェクトに置き換え可能であると想定された場合、悪いことが起こる可能性があります。
  2. オブジェクトが「同一」であるという間違いがなくても、置換を行うスレッドと同一に見えるオブジェクトが、他のスレッドと同一に見えない可能性があるという危険性が依然として存在する可能性があります。このシナリオが発生する可能性は低いですが、発生した場合、影響は非常に悪い可能性があります。

それでも、不変オブジェクトの置換を行うことにはいくつかの利点があります。たとえば、プログラムが長い文字列を保持する多くのオブジェクトを比較し、それらの多くが別々に生成されても互いに同一である場合、 a を使用しWeakDictionaryて個別の文字列インスタンスのプールを構築し、プール コピーへの参照を使用して、プール内の文字列と同一であることが判明した文字列。これを行うと、同一の多くの文字列が同じ文字列にマップされるため、それらの間で行われる可能性のある将来の比較が大幅に高速化されます。もちろん、前述のように、オブジェクトが適切に論理的に不変であること、比較が正しく行われることは非常に重要です。その点で問題があると、最適化すべきものが混乱に変わる可能性があります。

于 2012-07-02T00:54:26.347 に答える
1

String は不変ですが、リフレクションによって変化する可能性があります。ハッシュを final にすると、これが発生した場合に非常に混乱する可能性があります。ハッシュフィールドは、主にキャッシュとして存在し、計算を高速化する方法であるという点でも異なります。hashCode()実際には、定数ではなく、計算フィールドと見なす必要があります。

于 2012-07-01T13:12:59.023 に答える
0

オブジェクトを不変にするには クラスを final にし、そのすべてのメンバーを final にして、オブジェクトがクレートされると誰もその状態を変更できないようにする必要があります。メンバーを最終的ではなくプライベートにし、コンストラクター以外では変更しないことで、同じ機能を実現できます。

編集:

注意 : 文字列をハッシュする場合、Java はハッシュ値もハッシュ属性にキャッシュしますが、 それは結果がゼロ以外の場合のみです。

于 2012-07-01T13:11:40.840 に答える