2015年7月2日 星期四

複製物件

1.Object 有個clone() 方法,可以達到複製的目的
但還要implements Cloneable,不然會報CloneNotSupportedException

2.可以將clone()的protected 改成 public,也可將回傳的 Object 改成指定的物件

3.原形模式


※無集合

public class ClassPrototype implements Cloneable {
    private String xxx;
    private Integer ooo;
    
    // setter/getter...
    
    public ClassPrototype() {
        System.out.print("default constructor!");
    }
    
    @Override
    protected ClassPrototype clone() throws CloneNotSupportedException {
        return (ClassPrototype) super.clone();
    }
}




ClassPrototype prototype = new ClassPrototype();
prototype.setXxx("banana");
prototype.setOoo(20);
    
ClassPrototype copyPrototype;
try {
    copyPrototype = prototype.clone();
    System.out.println(copyPrototype.getXxx());
    System.out.println(copyPrototype.getOoo());
    
    copyPrototype.setXxx("apple");
    System.out.println(prototype.getXxx());
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}

※結果:
default constructor!banana
20
banana

※第一行會呼叫建構子,但複製的時候不會

※基本型態和 String,用 == 是false,沒有什麼問題



※淺複製、深複製

假設複製的物件是傳集合,
複製好的物件會影響到原本的物件,就是淺複製;不會影響的就是深複製


※深複製


public class ClassPrototype implements Cloneable {
    private String xxx;
    private List<String> list;
    // setter/getter...
    
    public ClassPrototype() {
        System.out.print("default constructor!");
    }
    
    @Override
    protected ClassPrototype clone() throws CloneNotSupportedException {
        return (ClassPrototype) super.clone();
    }
    
    public static void main(String[] args) {
        ClassPrototype prototype = new ClassPrototype();
        prototype.setXxx("banana");
        prototype.setList(Arrays.asList("a", "b"));
    
        ClassPrototype copyPrototype;
        try {
            copyPrototype = prototype.clone();
            System.out.println(copyPrototype.getXxx());
            System.out.println(copyPrototype.getList());
   
            copyPrototype.setList(Arrays.asList("c", "d"));
            System.out.println(copyPrototype.getList());
            System.out.println(prototype.getList());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}




※淺複製

public class ClassPrototype implements Cloneable {
    private ArrayList<String> list;
    // setter/getter...
    
    public ClassPrototype() {
        System.out.println("default constructor!");
    }
    
    @SuppressWarnings("unchecked")
    @Override
    protected ClassPrototype clone() throws CloneNotSupportedException {
        ClassPrototype cp = (ClassPrototype) super.clone();
        // cp.list = (ArrayList<String>) list.clone();
        return cp;
    }
    
    public static void main(String[] args) {
        ClassPrototype prototype = new ClassPrototype();
        ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        prototype.setList(list);
    
        ClassPrototype copyPrototype;
        try {
            copyPrototype = prototype.clone();
            System.out.println(copyPrototype.getList());
    
            copyPrototype.getList().add("c");
            System.out.println(copyPrototype.getList());
            System.out.println(prototype.getList());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}


※想改成深複製,將註解打開即可,但因為 List 沒有實作 Cloneable,所以沒有 clone 方法可用,所以改了 ArrayList;也可用 cp.list.list = (ArrayList<String>) ((ArrayList<String>) list).clone();

※註解還沒打開時,使用 == 判斷會發現是 true

注意:用 add 是正常的,但如果用 copyPrototype.setList(xxx),或將 copyPrototype new 一個全新的 copyPrototype,用 == 都會是 false,類似 call by reference 的問題
但將 copyPrototype = null ,然後在去.方法居然沒報錯,很奇怪


※自定物件

自定的和上面的集合都一樣,如下

public class ClassPrototype implements Cloneable {
    private Book book;
    // setter/getter...
    
    @Override
    protected ClassPrototype clone() throws CloneNotSupportedException {
        ClassPrototype cp = (ClassPrototype) super.clone();
        cp.book = book.clone();
        return cp;
    }
    
    public static void main(String[] args) {
        ClassPrototype prototype = new ClassPrototype();
        Book book = new Book();
        book.setName("abc");
        prototype.setBook(book);
    
        ClassPrototype copyPrototype;
        try {
            copyPrototype = prototype.clone();
            System.out.println(copyPrototype.getBook().getName());
    
            // 1.setter方式是OK的
            // Book b = new Book();
            // b.setName("def");
            // copyPrototype.setBook(b);
    
            // 2.getter再 setter 也沒問題
            copyPrototype.getBook().setName("def");
            System.out.println(copyPrototype.getBook().getName());
            System.out.println(prototype.getBook().getName());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}
    
class Book implements Cloneable {
    private String name;
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    protected Book clone() throws CloneNotSupportedException {
        return (Book) super.clone();
    }
}


沒有留言:

張貼留言