Effective Java读书笔记之clone方法

来源:互联网 发布:太阳能电池知乎 编辑:程序博客网 时间:2024/05/17 05:52

我们都知道Java中所有的类都继承自Object类,Object类中实现了一个clone方法。按照常理来推断,它应该就是对类中的field进行copy一下然后返回就好了。但是Java的实现很奇葩,它首先检查当前类是不是实现了Cloneable接口,如果实现了,才进行clone这个操作,否则跑出CloneNotSupportedException,而且这个clone方法还是protected。因此,如果我们需要定义一个提供clone方法的类,需要干以下几件事:

  1. 实现Cloneable接口
  2. 将clone方法变成public,一个最基本的实现如下:
    public class A implements Cloneable{private Object obj;@Overridepublic A clone(){return (A)super.clone();}}
以上的实现有几点问题:
  1. 首先就是deep copy的问题,如:
    A b1 = new A();A b2 = b1.clone();System.out.println(b1.objA == b2.objA); // true

    这显然不是我们想看到的,因为a2.obj的改变会导致a1.obj的改变,因此上述方法应该改为

    public class A implements Cloneable{    private Object obj;    public A clone() throws CloneNotSupportedException    {A a = (A) super.clone();if (obj != null)    a.obj = new Object();return a;    }}
    当然对于不同的filed我们需要不同的clone方法。

  2. 其次,对于那些被设计于用于被继承类,我们应该这样实现:
    class A{    Object objA;    protected A clone() throws CloneNotSupportedException    {A a = (A) super.clone();if (objA != null)    a.objA = new Object();return a;    }}class B extends A implements Cloneable{    Object objB;    public B clone() throws CloneNotSupportedException    {B b = (B) super.clone();if (objB != null)    b.objB = new Object();return b;    }}
     也就是说,不要实现Cloneable接口,将clone方法保持protected的访问权限,这样就给了子类是否要实现clone的选择权。
  3. 最后,我们在实现clone方法的时候应该遵循以下几个契约:
    x.clone() != x //克隆出的object应该与被克隆的object独立x.clone().getClass() == x.getClass() //克隆出来的对象应该与源对象同类x.clone().equals(x) //克隆出的对象应该与源对象equal


Object类中的clone方法是用native方法实现的,而且那个Cloneable接口的使用方法也挺奇葩,它是作为一个标识而不是定义类的责任而存在的,因此书中建议如果我们要提供clone类似的功能,我们可以像C++中的那样提供copy构造函数,或者提供一个静态的copy方法,这样比较优雅和安全。这样做唯一的坏处就是要自己多写几行代码进行field到field的copy。