复制有关Java的构造函数问题

时间:2013-08-17 23:21:17

标签: java constructor copy-constructor

我对Java中的复制构造有疑问。考虑以下类;

在复制构造函数中我可以说new(Integer(other.id))来获取一个新的整数对象传递给构造函数,但我不能说新的T(other.data),因为编译器会说不能实例化类型T.如何确保当通用项目被复制构造时,它不会仅传递引用,使得2个对象将共享基础数据。

此外,在getLinks方法中,它正在执行一个新的并创建列表的新对象,但是要深层复制并创建列表中包含的项的新对象,还是只包含对现有对象的引用列出项目,使您有2个列表指向相同的数据。请参阅下面的评论/代码。提前感谢您的专业知识。

class DigraphNode<T>  
{
    Integer id;
    T data;
    ArrayList<DigraphNode<T> > links;

    public DigraphNode(Integer i) 
    {
        id = i; 
        links = new ArrayList<DigraphNode<T> >();
    }
    public DigraphNode(Integer i, T d) 
    { 
        id = i; data = d; 
        links = new ArrayList<DigraphNode<T> >();
    }

    public DigraphNode(DigraphNode<T> other)
    {
        other.id = new Integer(other.id);
        other.data = other.data; // line in question
        this.links=other.getLinks(); // also will create a new list with references
                                     // or will it deep copy the items contained in the list?
                                     // see getLinks() method below
    }

    public void setData (T d ) { data =  d; }
    public void addLink (DigraphNode<T> n) { links.add(n); }
    public void addLinks (ArrayList<DigraphNode<T> > ns) { links.addAll(ns); }

    public ArrayList<DigraphNode<T> > getLinks()
    {
        return new ArrayList<DigraphNode<T> >(links); 
    }

    public void printNode()
    {
        System.out.print("Id: " + id + " links: ");
        for ( DigraphNode<T> i : links )
        {
            System.out.print(i.id + " ");
        }
        System.out.println();
    }
}

3 个答案:

答案 0 :(得分:1)

  1. 您无法在尝试时new T(other.data)实例化,但如果clone()
  2. 您可以T implements Cloneable other.data
  3. 每次调用getLinks()都会创建一个新列表,引用包含在links中的对象,你必须在内部具有相同引用的不同列表(因此更改一个引用对象属性将反映到其他列表对象因为它们是同一个对象)
  4. 关于来自Oracle doc的ArrayList<> links = new ArrayList<>();

      

    实例变量的初始化块看起来就像静态   初始化块,但没有static关键字:   
          {
              //无论初始化需要什么代码都在这里       }
          Java编译器将初始化程序块复制到每个构造函数中。因此,这种方法可以用来共享一个块   多个构造函数之间的代码。

  5.      编辑:
      您可以定义一个静态方法(copy),尝试使用所有可能的策略来复制通用对象;最好的方法是定义你自己的接口来分离你自己的状态并模拟一种复制构造函数(如果你愿意,可以重用copy方法),否则通过序列化,或者,如上次尝试,使用克隆(但是clone()充满了陷阱)   您也可以使用此库:


    interface MyCloneableInterface<T> {
      T duplicate(T object) throws CopyException;
    }
    public static <T> T copy(T data) throws CopyException  {
      if(data == null) return null;
      if(data instanceof MyCloneableInterface) {
        return ((MyCloneabeInterface)data).duplicate(data);
      }
      if(data instanceof Serializable) {
        try {
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          ObjectOutputStream oos = new ObjectOutputStream(baos);
          oos.writeObject(this);
    
          ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
          ObjectInputStream ois = new ObjectInputStream(bais);
          return (CloneExample) ois.readObject();
        }
        catch(...) {//rethrow}
      }
      if(data instanceof Cloneable) {
        try {
          return (T)data.clone();
        }
        catch(CloneNotSupportedException e) {//rethrow}
      }
      // else you can look for copy-constructor via reflection or
      // cloning object field-by-field via reflection...
    }
    

答案 1 :(得分:1)

第一个问题:您无法实例化通用实例(换句话说,调用T的构造函数)。您应定义T implements Cloneable并致电clone或使用您自己的其他界面,如果T始终在您的控制之下。这个方法有很多陷阱,我建议你先阅读这个界面并熟悉陷阱(你可以在“Effective Java”一书中找到一个很好的章节)。此外,您并不总是可以保证此课程将使用T的{​​{1}}类型。

关于Cloneable - 您在开头实例化它然后在构造函数中覆盖它 - 为什么?删除初始化。 links的工作方式不是通过创建深层副本。含义 - 您将获得一个新列表,列表本身将与原始列表不同,但项目将是浅层副本

关于你的最后一个问题 - 正如我已经说过的那样,这是多余的。在开头删除初始化。您正在创建一个对象,从不使用它并将其留给垃圾收集。你可以做些什么来避免在每个构造函数中调用它是这样的:

getLinks

让其他构造函数调用此构造函数,例如:

public DigraphNode() {
    links = new ArrayList<DigraphNode<T> >();
}

答案 2 :(得分:0)

赞成所有有用的答案,但我在回答下面显示更新代码的问题。我想看看有人如何实现一个通用的副本,但没有一个发布的代码,所以我自己推出了。请参阅下面的答案。

import java.lang.reflect.*;
import java.util.*;

class MissingDigraphNodeException extends Exception 
{
    private static final long serialVersionUID = 1000L;
    public MissingDigraphNodeException(String message)
    {
        super(message);
    }
}

class CopyException extends Exception 
{
    private static final long serialVersionUID = 2000L;
    public CopyException(String message)
    {
        super(message);
    }
}

class DigraphNode<T>  
{
    Integer id;
    T data;
    ArrayList<DigraphNode<T> > links;

    public DigraphNode(Integer i) 
    {
        id = i; 
        links = new ArrayList<DigraphNode<T> >();
    }
    public DigraphNode(Integer i, T d) 
    { 
        id = i; data = d; 
        links = new ArrayList<DigraphNode<T> >();
    }

    public DigraphNode(DigraphNode<T> other)
    {
        try
        {
            this.data = copy(other.data);
        }
        catch (CopyException e)
        {
            e.printStackTrace();
        }
        this.links=other.getLinks();
        this.id = new Integer(other.id);
    }

    // is there a better way to copy a generic?
    @SuppressWarnings("unchecked")
    public T copy( T source ) throws CopyException
    {
        Class<?> clzz = source.getClass();
        Method meth;
        Object dupl = null;
        try {
            meth = clzz.getMethod("clone", new Class[0]);
            dupl = meth.invoke(source, new Object[0]);
        } catch (Exception e) 
        {
            e.printStackTrace();
            throw new CopyException("Error: Copying Generic of T");
        }   
        return (T) dupl;
    }

    public void setData (T d ) { data =  d; }
    public void addLink (DigraphNode<T> n) { links.add(n); }
    public void addLinks (ArrayList<DigraphNode<T> > ns) { links.addAll(ns); }

    public ArrayList<DigraphNode<T> > getLinks()
    {
        // return a new copy of the list
        ArrayList<DigraphNode<T> > l = new ArrayList<DigraphNode<T> >(); 
        for ( DigraphNode<T> i : links )
        {
            i.links.add(new DigraphNode<T>(i)); // use copy constructor
        }
        return l;
    }

    public void printNode()
    {
        System.out.print("Id: " + id + " links: ");
        for ( DigraphNode<T> i : links )
        {
            System.out.print(i.id + " ");
        }
        System.out.println();
    }
}