Java使用可变成员创建包装类不可变

时间:2016-06-25 18:28:18

标签: java immutability

目前我有一个名为MatrixValue的类,我想将其作为一个不可变对象,这样我与MatrixValue实例交互的所有方法都无法改变其内部矩阵。但是,问题是成员变量之一是一个名为RealMatrix的可变对象,它存储矩阵的所有实际数据。我已经在构造函数中进行了防御性复制,并且摆脱了任何mutator方法。这是我班级到目前为止的样子:

public final class MatrixValue extends ExpressionValue{

    /**
     * 
     */
    private static final long serialVersionUID = -4231050452116360135L;
    private final RealMatrix matrix;

    public MatrixValue(RealMatrix matrix){
        this.matrix = new Array2DRowRealMatrix(matrix.getData());
    }

    public MatrixValue(double[][] values){
        matrix = new Array2DRowRealMatrix(values);
    }

    public RealMatrix getMatrix() {
        return matrix;
    }

    @Override
    public String toString(){
        return matrix.getRowDimension() + "," + matrix.getColumnDimension();
    }
}

现在的问题是,如果有人调用matrixValue.getMatrix().setEntry(row, col, value);,他们实际上可以更改最终RealMatrix成员变量的值。但是,我仍然希望能够返回有关RealMatrix的信息。什么是解决课堂不变性问题的最佳方法?

编辑:还要记住,某些矩阵可能会占用大量内存,因此如果可能的话,我宁愿选择最小化重复不必要信息的解决方案。

2 个答案:

答案 0 :(得分:0)

考虑在RealMatrix上使用接口。可能的接口可能称为Matrix。然后,您可以使用RealMatrix,然后在您提供外部API的位置,而不是使用RealMatrix的返回类型返回Matrix。

class RealMatrix implements {
// getters and setters
}

interface Matrix  {
// only getters
}

答案 1 :(得分:0)

最简单的方法可能是从matrix方法返回getMatrix()的防御性副本。您可以每次克隆它并返回方法或在构造函数中复制,只返回方法中的相同副本。

但是,我会想到MatrixValue的意图是什么。如果它是RealMatrix的不可变表示,我会考虑为RealMatrixMatrixValue构建一个接口,如下所示:

interface Matrix {
  // getters for Matrix
}

RealMatrixMatrixValue将按如下方式实现界面:

public class RealMatrix implements Matrix {
    // getters for Matrix
    // setters for RealMatrix
}

public class MatrixValue extends ExpressionValue implements Martix {
    public MatrixValue(RealMatrix matrix){
        this.matrix = new Array2DRowRealMatrix(matrix.getData());
    }

    // getters for Matrix
}

这提供了RealMatrixMatrixValue相应地Matrix的可变和不可变表示的意图,并允许API调用者从MatrixValue获取值但不能修改它。

顺便说一下,在Array2DRowRealMatrix构造函数中创建MatrixValue的新实例可能不足以进行防御性复制,因为double[][]的数组可以在外部修改。您需要为数组的每个维使用Arrays.copyOfSystem.arraycopy制作数组的副本,以避免发生变异。