cloneを賢くオーバーライドせよ
Clonableインターフェース周辺について何となくわかってから読まないと意味がわからなさそうなので意味がわかってから続きをよむ
Object#clone()
- Objectのcloneをオーバーライドする。
- Clonableインターフェースを実装する。これをしないとCloneNotSupportedExceptionがスローされる。(cloneできますよマークをつける)
注意点
- フィールドもちゃんとクローンしないとだめ(同じ参照のフィールドをもったクローンができてしまう)
- newではなく親クラスのcloneでインスタンスを作ること。(親クラスが持ってるフィールドがクローンされない)
リストの番号がリセットされないぞ。
Javaクローンメモ(Hishidama's Java Cloneable Memo)
Java - cloneメソッドの正しい実装方法 - Qiita
javadocのcloneの意味があんまりわからん
import java.util.ArrayList; import java.util.List; public class Example implements Cloneable{ public List<Integer> list = new ArrayList<>(); public static void main(String[] args){ Example ex1 = new Example(); try { //Exampleがcloneをオーバーライドしていない場合シャローコピーされる System.out.println(((Example) ex1.clone()).list == ex1.list); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } }
Exampleが親クラスを持っていた時super.cloneを呼び出したらExample固有の(親クラスがもっていない)フィールドはシャローコピーされるの?
public class ExampleChild extends Example { String str = null; public ExampleChild clone() throws CloneNotSupportedException{ return (ExampleChild) super.clone(); } public static void main(String [] args) throws CloneNotSupportedException{ ExampleChild ex = new ExampleChild(); ex.str = "unko"; System.out.println(ex.clone().str);// unkoが出力される } }
された。
親クラスがcloneをコンストラクタで実装していた場合
import java.util.ArrayList; import java.util.List; public class ExampleChild extends Example { public static void main(String [] args) throws CloneNotSupportedException{ ExampleChild ex = new ExampleChild(); //true System.out.println(ex.getClass() != ex.clone().getClass()); } } class Example implements Cloneable{ public List<Integer> list = new ArrayList<>(); @Override public Example clone(){ Example ex = new Example(); //フィールドのコピーは省略 return ex; } }
親クラスがsuper.cloneで実装していた場合
import java.util.ArrayList; import java.util.List; public class ExampleChild extends Example { public static void main(String [] args) throws CloneNotSupportedException{ ExampleChild ex = new ExampleChild(); //false System.out.println(ex.getClass() != ex.clone().getClass()); } } class Example implements Cloneable{ public List<Integer> list = new ArrayList<>(); @Override public Example clone() throws CloneNotSupportedException{ return (Example) super.clone(); } }
- Cloneableを実装したクラスはpublicでcloneをオーバーライドし、そのクラス自身の型を返却しろ
- オーバーライドしたcloneメソッドはまずsuper.cloneを呼び出し、必要に応じてフィールドをいじれ
→いじらないと参照がコピーされるだけだから
→イミュータブルなオブジェクトへの参照とかprimitiveなフィールドは多分そのままでいいぞ
→でもオブジェクトの生成時刻とかユニークIDとかはだめだぞ
コピーコンストラクタとかコピーファクトリがcloneより優れている点
- よくわからん手順でインスタンスが生成されない
- 余計なチェック例外を投げない
- finalなフィールドがあっても大丈夫
- 入力の型が自由
- キャストもいらない
conversion constructorsってなんだ
Conversion Constructor(変換コンストラクタ) - Strategic Choice
new File(String path)みたいの
path.clone()でFile型は返せないよね、みたいなはなし