一部の言語で文字列が不変なのはなぜですか?


9

StringはJavaの不変クラスです。不変クラスは、インスタンスを変更できないクラスです。Javaプログラミング言語がStringクラスのオブジェクトを不変にすることを選択するのはなぜですか?


2
@PJTraillどうしても避けられないようです。他の言語の文字列リテラルは、たとえばCでは不変ではなく、Javaの他のクラスのオブジェクトは不変ではありません。
David Richerby

2
これはプログラミング言語の設計に関する質問です。それは私には話題のようです。
David Richerby

2
@DavidRicherby、文字列リテラルはCでは不変(C90用語では、プログラムがいずれかの形式の文字列リテラルを変更しようとした場合、動作は未定義です。言語にconstがないため、初期バージョンの一部で受け入れられました時々プログラマーが期待したが、それがサポートされたことはないと思います)私は誰もがリテラルを変更することを許していた初期のFORTRANの間違いから学んだと思います。一方、何かがおかしくないとしても、初期値がリテラルと同じである可変の新しいオブジェクトを作成するリテラルがあります。
AProgrammer

1
@AProgrammer Plentyは、なぜJavaがそのように設計されたのかについて書かれています。Stringクラスに関する設計上の決定について信頼できるものがないとしたら、私は驚きます。しかし、言語設計者がStringが不変である理由を一度も言わなかったとしても、それによって問題がトピックから外れたり、さらには悪いものになったりすることはありません。
David Richerby

1
@DavidRicherbyただし、質問が言語にとらわれないのであればもっと良いでしょう。これは、Java開発者の声明を引用することで回答できます。概念を説明する答えが必要です。
ラファエル

回答:


9

この問題は、クラスのインスタンスであるとはどういう意味かという概念と強く関連しています。厳密なオブジェクト指向の用語では、クラスには関連する不変条件があります。つまり、クラスの(パブリック)メソッドからの出口で常にtrueを保持する述語です。このような概念は、たとえば、継承が明確に定義されていることを保証する上で中心的な役割を果たします(これは、リスコフ置換の原則の一部です)。

Javaの最も厄介な問題の1つは、クライアントコードがクラスの不変条件を壊すのを防ぐのが難しいことです。

たとえば、次の 'ZipCode'クラスを考えます。

class ZipCode {
    private String zipCode;

    public ZipCode(String value){
        if(!isValidZipCode(value))
            throw new IllegalArgumentException();
        zipCode = value;
        assert(invariant());
    }

    public String get() { return zipCode; }

    public boolean invariant() {
        return isValidZipCode( zipCode );
    }
}

Stringが不変でない場合、ZipCodeのユーザーが「get」を呼び出してその後いつでも文字を変更できるため、不変条件が破られ、ZipCodeコンセプトのカプセル化によって提供される概念的な整合性が破壊されます。

この種の整合性は、大規模なシステムが有効であることを保証するために不可欠であるため、質問に対するこの回答は、実際には次のより広いものを要求します。

「JavaがC ++ constの類似物をサポートしないのはなぜですか、少なくとも、ライブラリクラスの多くの不変バージョンを提供しないのですか?」


7

文字列や日付などは当然のことながら値です。C ++の用語では、コピーコンストラクター、代入演算子、および等価演算子があることを期待しますが、それらのアドレスを取得することは決してありません。したがって、それらが個別にヒープに割り当てられるとは想定していません。仮想メソッドは意味がありません。

ドメインオブジェクトは当然参照です。C ++のものには、コピーコンストラクター、代入演算子、または等価演算子がありません(同一の場合にのみ等価です)。私たちはそれらのアドレスを取得することができ、ヒープが割り当てられることを期待しています。メソッドは一般に仮想です。

Javaには値クラスはありません。参照クラスのみです。値は不変オブジェクトで偽造されています。これは文字列には当てはまりますが、残念ながら日付には当てはまりません。Java日付の可変性により、頻繁に問題が発生し、現在は推奨されていません。たとえば、可変値はハッシュの基礎として使用できません。


まあ、変更可能な値ハッシュに使用できます、ハッシュコードに依存している場合は後で変更しない方がよいでしょう。
gnasher729 2016

6

Javaは、セキュリティが制限された環境でプログラムのコードのサブセクションを実行できるように設計されています。この要件が実装された方法は、特定の重要な操作(ファイルを開くなど)のパラメーターへのアクセス権が与えられ、操作を続行できるかどうかを尋ねるスレッドに「SecurityManager」を設定することでした。Java文字列が変更可能である場合、プログラムは2つのスレッドを作成することでこのような制限を回避できます。1つは許可されるファイルを開く操作を実行し、もう1つはファイル名を格納する文字列を許可されないものに変更しました。次に、セキュリティマネージャが元の文字列を読み取り、操作を受け入れる可能性があります。この操作は、2番目の(許可されていない)ファイルを開く前にファイルを開くコードに渡されます。

  • 不変文字列
  • 許容性を確認する前に、セキュリティ上重要な文字列の防御コピーを実行します。

後者の可能性は、そのようなすべての操作の実行を遅くし、実装にバグが含まれる可能性が高くなるため、不変の文字列を使用することが最も賢明な決定でした。

より一般的には、不変オブジェクトは、防御的なコピーを作成せずに共有できるため(セキュリティが重要でないコードでも、ソースデータが変更されたときにバグを防ぐために必要な場合がある)、この要件がなくても決定が可能なため、有用です。合理的なもの。


1
James Goslingがこの設計決定について完全に明確であったため、誰かがこれを指摘してくれてうれしいです。Javaは、ネットワーク経由で送信された信頼できないコードを実行できるように設計されています(Webブラウザーやデジタルセットトップボックスなど)。文字列を不変にする主な理由は、ベンダーまたはサイトマネージャー(およびJava標準ライブラリの実装者)が独自のカスタムセキュリティポリシーを簡単に実装できるようにするためです。不変文字列は、設計上、1つの潜在的な攻撃ベクトルを効果的に遮断します。
仮名
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.