web-dev-qa-db-ja.com

なぜJavaにコピーコンストラクタがないのですか?

なぜJavaはC++のようなコピーコンストラクターをサポートしないのですか?

65
Cuga

Javaが行います。それらは、C++のように暗黙的に呼び出されるわけではありません。それがあなたの本当の質問だと思います。

まず、コピーコンストラクターは次のようなものです。

public class Blah {
  private int foo;

  public Blah() { } // public no-args constructor
  public Blah(Blah b) { foo = b.foo; }  // copy constructor
}

これで、C++は次のようなステートメントで暗黙的にコピーコンストラクターを呼び出します。

Blah b2 = b1;

そのインスタンスでの複製/コピーは、Javaでは意味がありません。なぜなら、すべてのb1とb2は参照であり、C++のような値オブジェクトではないためです。C++では、このステートメントはオブジェクトの状態のコピーを作成しますJava単純に参照をコピーします。オブジェクトの状態はコピーされないため、暗黙的にコピーコンストラクターを呼び出しても意味がありません。

そして、それが本当にすべてです。

130
cletus

ブルース・エッケル から:

[コピーコンストラクタ]がJavaではなくC++で動作するのはなぜですか?

コピーコンストラクターは、オブジェクトのローカルコピーを自動的に作成するため、C++の基本的な部分です。しかし、上記の例は、Javaでは機能しないことを証明しています。どうして? Java操作するものはすべてハンドルですが、C++ではハンドルのようなエンティティを持つことができ、オブジェクトを直接渡すこともできます。これがC++コピーコンストラクタの目的です。オブジェクトを取得し、値で渡し、オブジェクトを複製したいので、C++では問題なく動作しますが、このスキームはJavaでは失敗するため、使用しないでください。

(ページ全体を読むことをお勧めします-実際には、代わりに here を開始してください。)

13
Dan Breslau

これに対する答えは非常に興味深いと思います。

たとえば、Javaすべてのオブジェクトがヒープ上にあり、ポインタがなくても「参照」があります。参照にはコピーの意味があり、Javaは内部的に参照カウントを追跡するため、そのガベージコレクターは安全に削除できるものを認識します。

コピー可能な参照を介してのみオブジェクトにアクセスするため、オブジェクトをコピーする必要がある実際の回数は大幅に削減されます(たとえば、C++では、オブジェクトを(値によって)関数に渡すだけで、新しいオブジェクトが= Javaオブジェクトへの参照のみが渡されます。)設計者は、おそらくclone()が残りの使用には十分であると考えました。

9
dicroce

これは私の意見です(正当な答えがあると確信しています)

C++のコピーコンストラクターは、コピーコンストラクターが透過的にアクティブ化されるため、値によってクラスのインスタンスを送信または返すときに主に役立ちます。

Javaすべてが参照によって返され、VMは動的割り当てを対象としているため、コピーコンストラクタの複雑さを正当化する理由はありませんでした。 。

さらに、すべてが参照であるため、開発者は独自の実装を提供し、フィールドを複製する方法を決定する必要があります。

2
Uri

代わりにclone()メソッドを作成するだけでよいと考えていたでしょうか?

1
Ryan

なんてことだ。浅いコピーで問題ない場合、[clone()]( http://Java.Sun.com/j2se/1.4.2/docs/api/Java/lang/Object.html# clone()) そしてそうでない場合は、C++のようにディープコピーを実装する必要があります。

唯一の実質的な違いは、コンストラクターではなくファクトリー・メソッドであるということですが、柔軟性とテスト容易性の点ではおそらく良いことです。

0
Aaron Maenpaa

私はC++プログラマーではありませんが、コピーコンストラクター、代入演算子、デストラクタという「3つのアミゴ」に関するルールを覚えているようです。持っている場合は、おそらく3つすべてが必要です。

言語にデストラクタがなければ、コピーコンストラクタを含めたくないのでしょうか?ただの推測。

0
David Hodgson

まあ、できます。暗黙的に作成されるわけではありません。推測しなければならなかった場合、おそらくJavaオブジェクトは常にヒープに割り当てられます。

C++では、デフォルトのコピーコンストラクターはメンバーごとの浅いコピーです。クラスが(rawポインターを介して)ヒープに割り当てられたメモリを所有している場合、これにより、コピーが元の内部を共有しますが、これは望みのものではありません。

Javaがこの動作をしていることを想像してください。オブジェクトであるフィールド(読み取り:本質的にすべて)を持つクラスはすべて間違った動作をするため、自分でオーバーライドする必要があります。 99%のケースで、誰もトラブルを保存していませんさらに、あなた自身のために微妙なトラップを作成したばかりです-誤ってデフォルトのコピーコンストラクタをオーバーライドするのを忘れてしまったことを想像してください。それを使用するために、コンパイラはまったく文句を言うことはありませんが、プログラムは実行時に誤動作します。

ディープコピーを実行するデフォルトのコピーコンストラクタを作成したとしても、それが特に役立つかどうかはわかりません。とにかくC++よりもJavaでより少ないコピーを実行する傾向があるだけでなく、フィールドをディープコピーしたくない場合もあります。

所有しているオブジェクトと、参照が必要なために参照を保持しているが、責任を負わないオブジェクトは同じであり、フィールドだけです。所有権と借用は第一級の概念ではありません。所有しているオブジェクトの場合は、それらをディープコピーする必要があります(不変でない場合は、この場合は気にする必要はありません)。また、参照を保持するだけのオブジェクトの場合は、参照をコピーします。

私は、すべてを単に無意識に深くコピーするコピーコンストラクターは、多くのクラスにも適さないと主張します。ただし、デフォルトで浅いコピーよりも確かに多く。

0
Kevin