- - PR -
Object クラスの clone メソッドが protected なので使いづらい
投稿者 | 投稿内容 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2003-03-20 17:34
>1.protectedである必然性はない。
>2.Cloneableには問題がある。 >この両者は独立していると思います。 これは同感. >unibonさんのおっしゃるように、clone()はpublicであるべきだと思います。 >親クラスがクローン不可能なのに、子クラスがクローン可能なのは変です。 これは全く反対.少なくともpublicにすべきではない. 親クラスで不可能なもの(機能が無い)が,子クラスで可能になる (機能が追加される)というのはごく自然なことのはず. 逆に子クラスのほうが制限されるほうが不自然で,むしろ 可能ならば避けるべきものと言えます. これはOOPの本なら普通に説明されてると思うし,ほとんど 常識のはず. #理論を延々と説明するのは手間がかかるのでパス. #それに上手く説明できる自信もないし. 第一,プログラムが面倒になります.なんせ全部実行時 エラーでしか検出できなくなるわけでしょ? >私は主にC++を使うのですが、リソースなどコピーできないメンバ変数を >持つクラスは、コピーコンストラクタと代入演算子をprivateに定義して C++は有名な欠陥言語なので,OOPの議論においてC++を例として 引用するのは無意味です.C++を使わざるを得ないOSがあるのは 知っていますが,だからと言ってC++の欠陥が変わるものでは ありません. | ||||||||||||
|
投稿日時: 2003-03-20 18:28
前に、色々試してみた時、clone()って、遅かったような記憶があります。 あらゆるケースを試したわけではないですが、例えば配列オブジェクトをclone()するのと、newしてから、System.arraycopy()するのでは、後者の方が断然速かったです。 私は、自分で作る時は、clone()は使いません。コピーコンストラクタを使います。 以下が可能なら、まだ使う気にもなるんですが。 public Object dup(Cloneable obj) { return obj.clone(); } clone()でないと、出来ない事が存在しないので... | ||||||||||||
|
投稿日時: 2003-03-20 18:50
>これは全く反対.少なくともpublicにすべきではない.
>第一,プログラムが面倒になります.なんせ全部実行時 >エラーでしか検出できなくなるわけでしょ? そうですね、ランタイムエラーよりも コンパイルエラーの方が望ましいのは確かです。 正直、clone()がSetインターフェースを介して呼べないのは、 望みの動作のようにも思えます。ただ、デフォルトの動作が 浅いコピーであり、TreeSetとHashSetの親クラスの動作に 問題がないのなら、publicにしたほうが単純な気がします。 >親クラスで不可能なもの(機能が無い)が,子クラスで可能になる >(機能が追加される)というのはごく自然なことのはず. >逆に子クラスのほうが制限されるほうが不自然で,むしろ >可能ならば避けるべきものと言えます. 普通は同感できるのですが、クローンだと異論があります。 普通クラスを定義するとき、private変数は自己以外から アクセスされないことを仮定して書くと思います。 もしこのクラスを継承し、Cloneableをインプリメントした場合、浅いコピーなので super.clone()をよべば親クラスのprivate変数が参照で共有されることになります。 ////////////////////////////////////// // あまりいい例ではありませんが、 // 普通にコーディングしたつもりです。 import java.util.HashSet; class Country { private final HashSet cities = new HashSet(); public void addCity(String name) { this.cities.add(name); } public void printMe() { System.out.println(this.cities); } } class CountryEx extends Country implements Cloneable { private final String name; public CountryEx(String name) { this.name = name; } public Object clone() throws CloneNotSupportedException { return super.clone(); } public void printMe() { System.out.println("[" + this.name + "]"); super.printMe(); } } public class MyClass { public static void main(String[] args) throws CloneNotSupportedException { CountryEx a = new CountryEx("France"); CountryEx b = (CountryEx) a.clone(); a.addCity("Paris"); a.printMe(); b.printMe(); } } こうすると、bには追加していないけれど [France] Paris [France] Paris となります。 そうすると、 1.親クラスがCloneableでない場合は super.clone() を呼ぶべきではない(子クラスの責任) 2.Cloneableでないクラスを定義するときは、継承クラスがCloneableをインプリメント することに備えた実装をする必要がある(親クラスの責任) のどちらかが必要になると思います。 1.はナンセンスです。 そもそも、HashSet extends AbstractSet implements Cloneable と JDK1.4.1で定義されていますが、その中で super.clone() が呼ばれています。 とすると、「AbstractSetはCloneableではないが、clone()を呼んでも安全だ」 という実装の詳細を把握していなくてはならないことになります。 なので、2.ということになるんですが、 私にはCloneableでないクラスで例外を送出するように clone()を実装するぐらいしか思いつきません。そうすると、 Cloneableの存在する理由が無くなってしまいます。 以上の点で、Cloneableはわからない点が多いと思います。 NotCloneableなら、まだ理解できますが・・・ [ メッセージ編集済み 編集者: ocean 編集日時 2003-03-20 18:56 ] | ||||||||||||
|
投稿日時: 2003-03-20 19:46
"shallow copy"ならわざわざclone()を使う必要はないのでは?
で期待する結果が得られるはず。 | ||||||||||||
|
投稿日時: 2003-03-20 21:04
>1.親クラスがCloneableでない場合は super.clone() を呼ぶべきではない(子クラスの責任)
>2.Cloneableでないクラスを定義するときは、継承クラスがCloneableをインプリメント >することに備えた実装をする必要がある(親クラスの責任) >のどちらかが必要になると思います。 多分2が正しいのでしょう. 「子クラスを作ることを許し,且つ,子クラスがclone()をサポートする ことを許す場合は,親クラスではそのことを想定して実装する必要がある.」 継承できるように設計されていない限り,一般には継承は不可能,というのが 原則だと思います.継承は万能じゃないんですよ. ただ,推論の方向性自体は正しいと思います. >以上の点で、Cloneableはわからない点が多いと思います。 >NotCloneableなら、まだ理解できますが・・・ で,正にこういうふうな結論に至るので,clone()はイマイチ設計が良くないと 言われたりするわけです.もし,もっと綺麗な設計が思いついたら,.... まあ,Javaについてはもはや手遅れですけどね. 次は軽く10〜20年後でしょう.全く異なる新言語が登場する時ですから. | ||||||||||||
|
投稿日時: 2003-03-26 09:23
unibon です。こんにちわ。
つぎのコードで試してみましたが、速度差はあまり感じませんでした。
このサンプルプログラムは論理的に 80MB(=200*100000*4バイト)のメモリを使いますが、 物理メモリが 512MB 程度の Windows 2000 のコンピュータで、 JDK 1.4.1 で -Xmx256m を指定して動かしてもさほど差がなかったです。 ただ arraycopy のほうが1%ほど速いような気もしないではなかったですが、 誤差の範囲かもしれません。 ループ回数などを増やすとガーベッジコレクションの影響が大きくなることもあり、 あまりうまく測定できていないのですが。
以下、かなり自信がないのですが、 これを拝見すると、そもそも Object.clone は abstract でも良いような気もします。 (ポリモーフィズムのためには Object クラスに、 まったく clone の定義がないと困りますが、 でも native の実装がある必要がないような気がします。) #以下、あとで追加。 でもそうしたら Object o = new Object(); ってできなくなりますね。 やっぱり abstract にはできないですね。でも Object.clone の動作として、 フィールドの浅いコピーがなくても良い、 すなわち、プリミティブなフィールドは 0 や false になり、 オブジェクト用のフィールドは null になる、 というのはありかもしれませんね。 でも、もしそういう仕様だとしたら、こんどは、 「なぜついでに浅いコピーしてくれないんだ。 手間がかかるわけでもなしデフォルトで浅いコピーはやってほしい。 特に配列のときなどは大変じゃないか。」 という不満が出るに決まっているので、今のような動作になるんでしょうね。 #あと、編集すると BB コードが崩れたので、BB コードの部分(CODE タグ)を編集しました。 [ メッセージ編集済み 編集者: unibon 編集日時 2003-03-26 15:22 ] | ||||||||||||
|
投稿日時: 2003-03-26 20:43
>つぎのコードで試してみましたが、速度差はあまり感じませんでした。
というより, >ループ回数などを増やすとガーベッジコレクションの影響が大きくなることもあり、 >あまりうまく測定できていないのですが。 が正解だと思います. クローンだろうがnewだろうが大量のインスタンスを次々と生成する処理 自体が本質的に遅いので,最適化を考えるなら無駄なインスタンス生成を 減らすようにアルゴリズムの変更を検討すべきです.インスタンス生成と GCに比べれば,cloneとarraycopyの差など誤差みたいなものなんでしょうね. 下手すると,内部的にarraycopyと同じ処理に最適化してる可能性さえあります. | ||||||||||||
|
投稿日時: 2003-07-11 20:28
すでに凍ったスレッドだと思いますが、認識が改まったので追記させていただきます。
C#では、次のように簡潔に実現できるようです。
Javaでこうできないのは、Cloneableがタグにすぎず、実際にはclone()を持っていないためです。なぜJavaがそのような設計にしたかは存じませんが・・・ 以前の私の返答は、C++の影響でしょうか、Objectがclone()を持つことを前提にしていたところがありました。でも実際にC#の設計を見てみると、悪夢を統べるものさんの
が正しかったのかなと思えました。 まだ、 class A class B : ICloneable, A がAの設計変更でICloneableを実装したとき、B::Clone()を変更しなくちゃいけないのでは?とか、疑問が残ってはいますが。。。 [ メッセージ編集済み 編集者: ocean 編集日時 2003-07-11 20:33 ] [ メッセージ編集済み 編集者: ocean 編集日時 2003-07-11 20:33 ] |