简体   繁体   中英

Java Create Wrapper Class Immutable with Mutable members

currently I have a class called MatrixValue that I want to make into an immutable object so that all my methods that interact with an instance of MatrixValue can't change its inner matrix. However, the problem is one of the member variables is a mutable object called RealMatrix that stores all the actual data of the matrix. I've already put in defensive copying into the constructor and gotten rid of any mutator methods. Here is what my class looks like so far:

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();
    }
}

The problem right now is that if someone calls matrixValue.getMatrix().setEntry(row, col, value); they are effectively able to change the value of the final RealMatrix member variable. However, I still want to be able to return information regarding the RealMatrix . What's the best approach to fixing this hole in the class's immutability?

EDIT : Also do keep in mind that some matrices can become memory intensive so if possible I would prefer a solution that minimizes duplicating unnecessary information if possible.

Consider using an interface on RealMatrix. A possible interface might be called Matrix. You internals representation could then use RealMatrix and then where you provide your external API you can instead of using the return type of RealMatrix return a Matrix instead.

class RealMatrix implements {
// getters and setters
}

interface Matrix  {
// only getters
}

The simplest way to do probably is to return a defensive copy of matrix from the getMatrix() method. You may clone it every time and return in the method or make a copy in the constructor and only return the same copy in the method.

However, I would think what the intention of MatrixValue is. If it is an immutable representation of RealMatrix , I would consider to compose an interface for RealMatrix and MatrixValue as follows:

interface Matrix {
  // getters for Matrix
}

RealMatrix and MatrixValue will implement the interface as follows:

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
}

This provide the intention that RealMatrix and MatrixValue are mutable and immtable representation of Matrix correspondingly and allow API callers to get the values from MatrixValue but cannot modify it.

By the way, create a new instance of Array2DRowRealMatrix in MatrixValue constructor may not enough to do a defensive copying because the array of double[][] can be modified externally. You need to make a copy of the array using Arrays.copyOf or System.arraycopy for each dimension of the array to avoid mutation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM