バックグラウンド
Javaでfinalを表示できる場所は3つあります。
クラスをfinalにすると、クラスのすべてのサブクラス化が防止されます。メソッドをfinalにすると、メソッドのサブクラスがメソッドをオーバーライドできなくなります。フィールドをfinalにすると、後で変更されることを防ぎます。
誤解
最終的なメソッドとフィールドの周りで起こる最適化があります。
最後の方法は、HotSpotがインライン化を介して簡単に最適化できるようにします。ただし、HotSpotは、他の方法で証明されるまでオーバーライドされないという前提で機能するため、メソッドが最終的でない場合でもこれを行います。SOの詳細
最終的な変数は積極的に最適化することができ、それについてはJLSセクション17.5.3で読むことができます。
ただし、その理解により、これらの最適化はいずれもクラスを最終的なものにすることではないことに注意する必要があります。クラスをファイナルにすることによるパフォーマンスの向上はありません。
クラスの最後の側面も不変性とは関係ありません。finalではない不変のクラス(BigIntegerなど)、または可変で finalのクラス(StringBuilderなど)を持つことができます。クラスがあればについての決定すべき最終的なものでは設計の問題です。
最終設計
文字列は、最もよく使用されるデータ型の1つです。それらはマップのキーとして検出され、ユーザー名とパスワードを保存し、キーボードまたはWebページのフィールドから読み取るものです。文字列はどこにでもあります。
地図
Stringをサブクラス化できる場合に何が起こるかを最初に検討するのは、誰かがStringのように見える可変Stringクラスを構築できることを認識することです。これはどこでもマップを台無しにします。
この架空のコードを考えてみましょう。
Map t = new TreeMap<String, Integer>();
Map h = new HashMap<String, Integer>();
MyString one = new MyString("one");
MyString two = new MyString("two");
t.put(one, 1); h.put(one, 1);
t.put(two, 2); h.put(two, 2);
one.prepend("z");
これは一般にマップで可変キーを使用する場合の問題ですが、私がそこに到達しようとしているのは、突然マップのブレークに関する多くのことです。エントリは、マップの適切な場所にありません。HashMapでは、ハッシュ値が変更されている(変更されるはずである)ため、正しいエントリにありません。TreeMapでは、ノードの1つが間違った側にあるため、ツリーが壊れています。
これらのキーにStringを使用することは非常に一般的であるため、Stringをfinalにすることでこの動作を防ぐ必要があります。
Javaで文字列が不変である理由を読むことに興味があるかもしれません。文字列の不変の性質についての詳細。
邪悪なひも
文字列にはさまざまな邪悪なオプションがあります。equalが呼び出されたときに常にtrueを返すStringを作成し、それをパスワードチェックに渡したかどうかを検討してください。または、MyStringへの割り当てにより、文字列のコピーが電子メールアドレスに送信されるようにしましたか?
これらは、文字列をサブクラス化する能力がある場合に非常に現実的な可能性です。
Java.lang文字列の最適化
前に述べたように、finalはStringを高速化しません。ただし、Stringクラス(および内の他のクラスjava.lang
)は、フィールドおよびメソッドのパッケージレベルの保護を頻繁に使用して、他のjava.lang
クラスが常にStringのパブリックAPIを通過するのではなく、内部を調整できるようにします。StringBufferによって使用される範囲チェックなしのgetCharsやlastIndexOfなどの関数、または基になる配列を共有するコンストラクター(メモリの問題によりJava 6が変更されたことに注意してください)。
誰かがStringのサブクラスを作った場合、(それはの一部であった場合を除き、これらの最適化を共有することができないjava.lang
、あまりにも、それはだ密封されたパッケージ)。
拡張のために何かを設計するのは難しい
拡張可能なものを設計するのは困難です。それは、何か他のものが変更できるように内部の一部を公開しなければならないことを意味します。
拡張可能な文字列では、メモリリークを修正できませんでした。それらの部分はサブクラスに公開されている必要があり、そのコードを変更すると、サブクラスが壊れることになります。
Javaは下位互換性を誇り、コアクラスを拡張することで、サードパーティのサブクラスとの計算可能性を維持しながら、物事を修正する機能の一部を失います。
Checkstyleには、すべてのクラスが次のいずれかであることを強制する「DesignForExtension」と呼ばれる、強制するルール(内部コードを書くときに本当にイライラさせられます)があります。
合理的な理由は次のとおりです。
このAPIデザインスタイルは、サブクラスによる破損からスーパークラスを保護します。欠点は、サブクラスの柔軟性が制限されていることです。特に、スーパークラスでのコードの実行を防ぐことはできませんが、これは、サブクラスがスーパーメソッドの呼び出しを忘れてスーパークラスの状態を破壊できないことも意味します
実装クラスの拡張を許可すると、サブクラスが基になるクラスの状態を破壊し、スーパークラスが与えるさまざまな保証が無効になる可能性があることを意味します。Stringのような複雑なものの場合、その一部を変更すると何かが壊れることはほぼ確実です。
開発者hurbis
開発者であることのその部分。各開発者が独自のutilsコレクションを使用して独自のStringサブクラスを作成する可能性を考慮してください。ただし、これらのサブクラスを相互に自由に割り当てることはできません。
WleaoString foo = new WleaoString("foo");
MichaelTString bar = foo; // This doesn't work.
この方法は狂気につながります。あらゆる場所でStringにキャストし、StringがStringクラスのインスタンスであるかどうかを確認します。そうでない場合は、そのクラスに基づいて新しいStringを作成します。しないでください。
私は確かにあなたが良いのStringクラスを書くことができますよ...しかし、C ++を書くとに対処する必要があり、それらの狂気の人々に複数の文字列の実装を書いたままstd::string
とchar*
し、ブーストやから何かSString、およびすべての残りの部分。
Java String Magic
Javaが文字列を使用して行う魔法の機能がいくつかあります。これらにより、プログラマーは対処しやすくなりますが、言語にいくつかの矛盾が生じます。Stringでサブクラスを許可すると、これらの魔法のようなものをどのように扱うかについて非常に重要な考えが必要になります。
文字列リテラル(JLS 3.10.5)
できるようにするコードを持つ:
String foo = "foo";
これは、整数などの数値型のボクシングと混同しないでください。あなたはできないが1.toString()
、あなたはできる"foo".concat(bar)
。
+
オペレータ(JLS 15.18.1)
Javaの他の参照型では、演算子を使用できません。文字列は特別です。文字列連結演算子はコンパイラレベルでも機能するため、実行時ではなくコンパイル時に"foo" + "bar"
なり"foobar"
ます。
文字列変換(JLS 5.1.11)
すべてのオブジェクトは、文字列コンテキストで使用するだけで文字列に変換できます。
文字列インターン(JavaDoc)
文字列クラスは、文字列のプールにアクセスして、コンパイルタイプで文字列リテラルを入力したオブジェクトの標準的な表現を持つことができます。
Stringのサブクラスを許可すると、プログラミングを容易にするStringを含むこれらのビットは、他のString型が可能な場合に非常に困難または不可能になることを意味します。