java中包含对象的两个dim数组的深度副本没有按预期工作



这是对前一篇文章的重写。我现在写了一个全新的项目,只是为了展示我不理解的行为。下面是代码。这个问题在本文末尾的测试输出中变得显而易见。我的假设是错误位于标有/*PROBLEMATIC*/的行,但我不知道这怎么会是错误的。

我想做的是:

  • 创建板对象板

  • 创建另一个名为boardB的对象,它是boardA的深层副本,因此它在其数组pos[][]中引用了不同的Square对象(正如输出告诉的那样,它确实引用了)。

因此,当我输出对象ID(使用hashCode())时(就像在第二个Board副本构造函数中一样),我希望为每个对象获得不同的哈希值(我确实得到了),也为每个Square对象的父对象获得不同哈希值(但我没有)。我希望产出能澄清这一点。

有什么想法吗?谢谢

以下是完整的测试代码:

public class CloneTest {
    public static void main(String[] args) {
        Board boardA = new Board();
        Board boardB = new Board(boardA.pos);
        System.out.println("boardA=" + boardA.hashCode());
        System.out.println("boardB=" + boardB.hashCode());
    }
}
class Board {
    final int boardSize = 2;
    Square[][] pos;
    /* create empty board */
    public Board() {
        pos = new Square[boardSize][boardSize];
        for(int row=0; row < boardSize; row++)
            for(int col=0;col < boardSize; col++) {
                pos[row][col] = new Square(row,col);
            }
    }
    /* copy existing board */
    public Board(Square[][] initpos) {
        pos = new Square[boardSize][boardSize];
        for(int row=0; row < boardSize; row++)
            for(int col=0;col < boardSize; col++) {
                System.out.println("initpos["+row+"]["+col+"]=" + initpos[row][col].hashCode() + 
                        " gh=" + initpos[row][col].gameHash());
                try {
                    this.pos[row][col] = initpos[row][col].clone(); /* PROBLEMATIC? */
                } catch (CloneNotSupportedException e) {
                }
                System.out.println("this.pos["+row+"]["+col+"]=" +  this.pos[row][col].hashCode() + 
                        " gh=" + this.pos[row][col].gameHash());
            }
    }
    class Square implements Cloneable {
        int row;
        int col;
        public Square(int r, int c) {
            row = r;
            col = c;
        }
        public Square clone() throws CloneNotSupportedException {
            Square newsquare = (Square) super.clone(); /* WRONG?*/
            return newsquare;
        }
        public int gameHash() {
            return Board.this.hashCode();
        }
    }
}

以下是输出示例:

initpos[0][0]=1598553873 gh=1874519541
this.pos[0][0]=1464824097 gh=1874519541
initpos[0][1]=546069071 gh=1874519541
this.pos[0][1]=1585252666 gh=1874519541
initpos[1][0]=1659432780 gh=1874519541
this.pos[1][0]=716609871 gh=1874519541
initpos[1][1]=973809521 gh=1874519541
this.pos[1][1]=843745660 gh=1874519541
boardA=1874519541
boardB=998786479

正如您所看到的,initpos[0]和this.pos[0]行的gh值是相同的(1874519541)。我希望这个.pos[0][0]的gh值是998786479,这是板B的id。

因此,克隆的Square对象(它有一个不同的id)仍然认为它属于第一个Board对象。但是,既然我把它分配给构造函数内的新Board对象,那怎么可能呢??

Java的clone()很棘手(如果不是邪恶的话),内部类更是如此。

我认为这适用于:

"正确"的方法是定义内部具有封闭类的参数的类。

或者更好的方法是,忘记克隆并编写一个复制构造函数。

SquareBoard的内部类。内部类的每个实例都有一个对其外部类实例的隐式引用。Board.this就是您的情况。如果没有外部实例,这样的对象也不可能存在。

因此,内部类的对象属于外部类对象的一个实例。他们属于哪一种情况不会改变。即使是,尤其是如果你克隆它们,因为克隆会克隆一切。

从新实例中创建手动副本,而不是使用.clone()

最简单的方法是提供一个复制构造函数。这是一个构造函数,它接受自己类型的对象。这样,您也不需要泄露实现细节(您的主要方法需要知道在板中隐藏了一个数组)。

public class CloneTest {
    public static void main(String[] args) {
        Board boardA = new Board();
        Board boardB = new Board(boardA);
    }
}
class Board {
    final int boardSize = 2;
    Square[][] pos;
    /* create empty board */
    public Board() {
        pos = new Square[boardSize][boardSize];
        for (int row = 0; row < boardSize; row++)
            for (int col = 0; col < boardSize; col++) {
                pos[row][col] = new Square(row, col);
            }
    }
    /** A copy constructor */
    public Board(Board other) {
        pos = new Square[boardSize][boardSize];
        for (int row = 0; row < boardSize; row++)
            for (int col = 0; col < boardSize; col++) {
                this.pos[row][col] = new Square(other.pos[row][col]);
            }
    }
    class Square {
        int row;
        int col;
        /** A Copy Constructor */
        public Square(Square other) {
            row = other.row;
            col = other.col;
        }
        public Square(int r, int c) {
            row = r;
            col = c;
        }
        public int gameHash() {
            return Board.this.hashCode();
        }
    }
}

最新更新