Stringクラスはどのように+演算子をオーバーライドしますか?


129

Stringがクラスであるのに、Javaで+演算子を使用してStringを追加できるのはなぜですか?ではString.javaコード私はこの作業のための任意の実装を見つけることができませんでした。この概念はオブジェクト指向に違反していますか?


21
プラス(+)演算子はJavaの言語機能です。
adatapost 2012

18
それはすべてコンパイラの魔法です。Javaでは演算子のオーバーロードを実行できません。
ライオン

18
奇妙なことに、Stringは言語(java.lang.String)の外部のライブラリとして実装されています、言語の内部では特定のサポートあります。それは正しくありません。
2012


@ 13renあなたは間違っている。文字列は、言語を表すjava.langクラスの一部です-Java言語 このパッケージのすべてのクラスは特別です。それらを使用するためにjava.langをインポートする必要がないことに注意してください。

回答:


156

Javaで次の単純な式を見てみましょう

int x=15;
String temp="x = "+x;

コンパイラ"x = "+x;StringBuilder内部的に変換し.append(int)、文字列に整数を「追加」するために使用します。

5.1.11。文字列変換

文字列変換により、任意の型をString型に変換できます。

プリミティブ型Tの値xは、適切なクラスインスタンス作成式(§15.9)への引数としてそれを与えるかのように、最初に参照値に変換されます。

  • Tがブール値の場合は、新しいBoolean(x)を使用します。
  • Tがcharの場合、new Character(x)を使用します。
  • Tがbyte、short、またはintの場合、new Integer(x)を使用します。
  • Tが長い場合は、新しいLong(x)を使用します。
  • Tが浮動小数点数の場合、新しいFloat(x)を使用します。
  • Tがdoubleの場合は、新しいDouble(x)を使用します。

この参照値は、文字列変換によって文字列型に変換されます。

これで、参照値のみを考慮する必要があります。

  • 参照がnullの場合、文字列 "null"(4つのASCII文字n、u、l、l)に変換されます。
  • それ以外の場合、変換は、引数なしで参照されるオブジェクトのtoStringメソッドを呼び出したかのように実行されます。ただし、toStringメソッドの呼び出し結果がnullの場合は、代わりに文字列「null」が使用されます。

toStringメソッドは、原始クラスObject(§4.3.2)によって定義されます。多くのクラス、特にBoolean、Character、Integer、Long、Float、Double、Stringがそれをオーバーライドします。

文字列変換コンテキストの詳細については、§5.4を参照してください。

15.18.1。

文字列連結の最適化: 中間文字列オブジェクトの作成と破棄を回避するために、実装は1つのステップで変換と連結を実行することを選択できます。繰り返し文字列連結のパフォーマンスを向上させるために、JavaコンパイラはStringBufferクラスまたは同様の手法を使用して、式の評価によって作成される中間のStringオブジェクトの数を減らすことができます。

プリミティブ型の場合、実装は、プリミティブ型から文字列に直接変換することにより、ラッパーオブジェクトの作成を最適化することもできます。

最適化されたバージョンは、実際には最初に完全にラップされた文字列変換を行いません。

これは、プリミティブを変換しなくても、コンパイラーが使用する最適化されたバージョンの良い例です。コンパイラーがバックグラウンドで物事をStringBuilderに変更しているのを見ることができます。

http://caprazzi.net/posts/java-bytecode-string-concatenation-and-stringbuilder/


このJavaコード:

public static void main(String[] args) {
    String cip = "cip";
    String ciop = "ciop";
    String plus = cip + ciop;
    String build = new StringBuilder(cip).append(ciop).toString();
}

これを生成します-2つの連結スタイルがまったく同じバイトコードになる方法を確認してください:

 L0
    LINENUMBER 23 L0
    LDC "cip"
    ASTORE 1
   L1
    LINENUMBER 24 L1
    LDC "ciop"
    ASTORE 2

   // cip + ciop

   L2
    LINENUMBER 25 L2

    NEW java/lang/StringBuilder
    DUP
    ALOAD 1
    INVOKESTATIC java/lang/String.valueOf(Ljava/lang/Object;)Ljava/lang/String;
    INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    ALOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;

    ASTORE 3

    // new StringBuilder(cip).append(ciop).toString()

   L3
    LINENUMBER 26 L3

    NEW java/lang/StringBuilder
    DUP
    ALOAD 1
    INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    ALOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;

    ASTORE 4
   L4
    LINENUMBER 27 L4
    RETURN

上記の例と、特定の例のソースコードに基づくバイトコードがどのように生成されるかを見ると、コンパイラーが次のステートメントを内部的に変換していることがわかります。

cip+ciop; 

new StringBuilder(cip).append(ciop).toString();

つまり、+文字列連結の演算子は、より詳細なStringBuilderイディオムの省略形です。


3
おかげで、私はjvmバイトコードに精通していませんが、String plus = cip + ciop;のコードを生成しました。および文字列ビルド= new StringBuilder(cip).append(ciop).toString(); 同じです。そして私の質問は、この操作がオブジェクトの向きに違反しているということですか?
Pooya

7
いいえ、ありません。オペレーターのオーバーロード(C ++や一部の言語など)にはいくつかの欠点があり、Java設計者はそれがやや紛らわしい概念であると感じ、Javaから省略しました。私にとって、オブジェクト指向言語には、Javaが持っている継承、ポリモーフィズム、およびカプセル化という主要な概念が必要です。
ライオン

2
はい、しかし私はこの演算子がStringクラスのためにオーバーロードしたと思います
Pooya

3
はい、JavaではString型の連結に演算子のオーバーロードが使用されますが、独自の演算子を定義することはできません(C ++、C#、およびその他のいくつかの言語の場合と同様)。
ライオン

5
@Pooya:実際の "int / int"と "int / float"はすでに演算子のオーバーロードになっているため、Cでもそれが可能です。ただし、C(およびJava)にはないユーザー定義の演算子のオーバーロード:演算子の使用方法を(CとJavaの両方で)定義するのは、言語の定義だけです(そして、その違いは、コンパイラ)。C ++は、ユーザー定義の演算子のオーバーロード(多くの場合、単に「演算子のオーバーロード」と呼ばれる)を許可するという点で異なります。
Joachim Sauer

27

演算子のオペランドをチェックするのはJavaコンパイラ機能です+。そして、オペランドに基づいてバイトコードを生成します:

  • 文字列の場合、文字列を連結するコードを生成します
  • 数値の場合は、数値を追加するコードを生成します。

これはJava仕様が言うことです:

演算子+および-は、加算演算子と呼ばれます。AdditiveExpression:MultiplicativeExpression AdditiveExpression + MultiplicativeExpression AdditiveExpression-MultiplicativeExpression

加算演算子は同じ優先順位を持ち、構文的には左結合です(左から右にグループ化されます)。いずれかのオペランドのタイプの場合+、オペレータはString、動作は、文字列連結です。

それ以外の場合、演算子の各オペランドの+型は、プリミティブ数値型に変換可能な型(§5.1.8)でなければなりません。そうでないと、コンパイル時エラーが発生します。

いずれの場合も、2項-演算子の各オペランドの型は、プリミティブ数値型に変換可能な型(§5.1.8)である必要があります。そうしないと、コンパイル時エラーが発生します。


7
仕様からの引用は、この質問にはまったく関係ありません。
アーネストフリードマンヒル

これは、「+演算子のいずれかのオペランドの型が文字列の場合、演算は文字列連結です。それ以外の場合、+演算子の各オペランドの型は、変換可能な型である必要があります(§5.1.8 )プリミティブ数値型に変換しないと、コンパイル時エラーが発生します。それが関係ない理由を教えてください。
Ramesh PVK

7
それがどのように実装されているかについては何も言われていない、それが問題です。投稿者はすでに機能が存在することを理解していると思います。
アーネストフリードマンヒル

14

Stringクラスはどのように+演算子をオーバーライドしますか?

そうではありません。コンパイラはそれを行います。厳密に言えば、コンパイラは文字列オペランドの+演算子をオーバーロードします。


6

まず、(+)はオーバーライドされずにオーバーロードされます

Java言語では、文字列連結演算子(+)が特別にサポートされています。これは、Java文字列オブジェクト用にオーバーロードされています。

  1. 左側のオペランドが文字列の場合、連結として機能します。

  2. 左側のオペランドが整数の場合、加算演算子として機能します


3
(2)左のオペランドが整数の場合、それは自動的にボックス化解除されint、Javaの通常の規則が適用されます。
ローンの侯爵2012

2
引用の下に示されている2つのルールは間違っています:私はそれらが正しいと信じています。少なくとも1つの文字列=連結
mwfearnley 2013

4

Java言語は、文字列連結演算子(+)および他のオブジェクトの文字列への変換を特別にサポートしています。文字列の連結は、StringBuilder(またはStringBuffer)クラスとそのappendメソッドを介して実装されます。


4

誰もがすでに書いているように、+適用されたときの演算子の意味はString言語によって定義されます。これは十分説得力があるとは思えないので、これを考慮してください。

int、float、doubleはすべてバイナリ表現が異なるため、2つのfloatを追加するのとは異なり、2つのintを追加することは、ビット操作の点で異なる操作です。浮動小数点数の場合、仮数と指数を別々に扱う必要があります。

したがって、原則として、「追加」は「追加」されるオブジェクトの性質に依存します。Javaは、文字列とintおよびfloat(long、double、...)に対してそれを定義します。


3

+オペレータは、通常に置き換えられてStringBuilder、コンパイル時に。この問題の詳細については、この回答を確認してください。


これが事実である場合、StringBuilderがパブリックで使用するために存在する理由は何ですか?+演算子が置き換えられない場合はありますStringBuilderか?
Cemre 2013

2
あなたが尋ねたい質問は、「なぜ+演算子が公共で使用するために存在するのか」ということです。他の質問については正確にはわかりませんが、そのようなケースはないと思います。
蠍座

要素が2つしかない場合、コンパイラは代わりにconcat()を使用します。また、プログラマーが長いネストされた/ループされたコードで文字列を構築している場合、コンパイラーはconcat()をStringBuilderに置き換える(または複数のStringBuilderを使用してそれらを一緒に追加する)ことに失敗します-単一の明示的なStringBuilderを使用するとパフォーマンスが向上します。
user158037
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.