使Java类不可变

时间:2017-08-28 19:44:38

标签: java immutability

我无法在Java中创建一个不可变的类。我知道我的构造函数可能会遇到一些问题,也可能是我的getter方法。从我所读到的,听起来我需要制作不引用原始对象的新对象。

如果可能的话,我想要一些帮助,引导我如何使这个类不可变。

    public class FMatrix implements Matrix {

  private final int rows, columns;
  private final int[][] matrix;
  //TODO: implement interface.

  public FMatrix(int[][] matrix) {
    int[][] matrix1;
    if (matrix.length == 0) {
      rows = 0;
      columns = 0;
    } else {
      rows = matrix.length;
      columns = matrix[0].length;
    }
    this.matrix = new int[rows][columns];
    //this.matrix = matrix;
  }

  /**
   * Returns the element at particular point in the matrix.
   * @param y y position
   * @param x x position
   * @return element
   */
  @Override
  public int getElement(int y, int x) {
    return matrix[y][x];
  }

  /**
   * Returns the number of rows in the matrix.
   * @return rows
   */
  @Override
  public int getRows() {
    return rows;
  }

  /**
   * Returns the number of columns in the matrix.
   * @return columns
   */
  @Override
  public int getColumns() {
    return columns;
  }

  /**
   * Returns this matrix scaled by a factor. That is, computes kA where k is a
   * constant and A is a matrix (this object).
   *
   * @param scalar scalar
   * @return matrix
   */
  @Override
  public Matrix scale(int scalar) {

    int value;
    int[][] mat1;
    int newRows = getRows();
    int newColumns = getColumns();

    mat1 = new int[newRows][newColumns];
    //this.matrix = new int[newRows][newColumns];
    FMatrix newMatrix = new FMatrix(mat1);
    for (int i = 0; i < getRows(); i++) {
      for (int j = 0; j < getColumns(); j++) {
        value = getElement(i, j) * scalar;
        mat1[i][j] = value;
      }
    }

    return newMatrix;
  }

  /**
   * Returns this matrix added with another matrix. That is, computes A+B where
   * A and B are matrices (this object, and another respectively).
   * @param other addend
   * @return matrix
   * @throws RuntimeException if matrices do not have matching dimensions.
   */
  @Override
  public Matrix plus(Matrix other) {
    int value;
    int newRows = getRows();
    int newColumns = getColumns();

    if (newRows != other.getRows() || newColumns != other.getColumns()) {
      throw new RuntimeException();
    }
    FMatrix newMatrix = new FMatrix(matrix);
    for (int i = 0; i < newRows; i++) {
      for (int j = 0; j < newColumns; j++) {
        value = getElement(i, j) + other.getElement(i, j);
        this.matrix[i][j] = value;
      }
    }
    return newMatrix;
  }

  /**
   * Returns this matrix subtracted by another matrix. That is, computes A-B
   * where A and B are matrices (this object, and another respectively).
   * @param other subtrahend
   * @return matrix
   * @throws RuntimeException if matrices do not have matching dimensions.
   */
  @Override
  public Matrix minus(Matrix other) {
    int value;
    int newRows = getRows();
    int newColumns = getColumns();

    //throw exception if dimensions do not match
    if (newRows != other.getRows() || newColumns != other.getColumns()) {
      throw new RuntimeException("Dimensions do not match");
    }
    FMatrix newMatrix = new FMatrix(matrix);
    for (int i = 0; i < newRows; i++) {
      for (int j = 0; j < newColumns; j++) {
        value = getElement(i, j) - other.getElement(i, j);
        this.matrix[i][j] = value;
      }
    }
    return newMatrix;

  }

  /**
   * Returns true if this matrix matches another matrix.
   * @param other another matrix
   * @return equality
   */
  @Override
  public boolean equals(Object other) {
    if (other == this) {
      return true;
    } else {
      return false;
    }

  }

  /**
   * Returns a string representation of this matrix. A new line character will
   * separate each row, while a space will separate each column.
   * @return string representation
   */
  @Override
  public String toString() {
    String str = "";
    for (int i = 0; i < rows; i++) {
      for (int j = 0; j < columns; j++) {
        str += getElement(i, j) + " ";
        if (j == columns - 1) {
          str += "\n";            //add a new line for next row
        }
      }
    }
    return str;
  }

主要方法

public static void main(String [] args){

int[][] data1 = new int[0][0];
int[][] data2 = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int[][] data3 = {{1, 4, 7}, {2, 5, 8}, {3, 6, 9}};

Matrix m1 = new FMatrix(data1);
Matrix m2 = new FMatrix(data2);
Matrix m3 = new FMatrix(data3);

System.out.println("m1 --> Rows: " + m1.getRows() + " Columns: " + m1.getColumns());
System.out.println("m2 --> Rows: " + m2.getRows() + " Columns: " + m2.getColumns());
System.out.println("m3 --> Rows: " + m3.getRows() + " Columns: " + m3.getColumns());

//check for reference issues
System.out.println("m2 -->\n" + m2);
data2[1][1] = 101;
System.out.println("m2 -->\n" + m2);

//test equals
System.out.println("m2==null: " + m2.equals(null));             //false
System.out.println("m3==\"MATRIX\": " + m2.equals("MATRIX"));   //false
System.out.println("m2==m1: " + m2.equals(m1));                 //false
System.out.println("m2==m2: " + m2.equals(m2));                 //true
System.out.println("m2==m3: " + m2.equals(m3));                 //false

//test operations (valid)
System.out.println("2 * m2:\n" + m2.scale(2));
System.out.println("m2 + m3:\n" + m2.plus(m3));
System.out.println("m2 - m3:\n" + m2.minus(m3));

//test operations (invalid)
//System.out.println("m1 + m2" + m1.plus(m2));
//System.out.println("m1 - m2" + m1.minus(m2));

}

这是main中测试的输出。问题是在调用.scale()方法后不应更改m2对象。

m1 --> Rows: 0 Columns: 0
m2 --> Rows: 3 Columns: 3
m3 --> Rows: 3 Columns: 3
m2 -->
1 2 3 
4 5 6 
7 8 9 

m2 -->
1 2 3 
4 101 6 
7 8 9 

m2==null: false
m3=="MATRIX": false
m2==m1: false
m2==m2: true
m2==m3: false
2 * m2:
2 4 6 
8 202 12 
14 16 18 

m2 + m3:
2 6 10 
6 106 14 
10 14 18 

m2 - m3:
1 2 3 
4 101 6 
7 8 9 

1 个答案:

答案 0 :(得分:0)

您的操作方法似乎有错误。考虑scale

FMatrix newMatrix = new FMatrix(mat1);
for (int i = 0; i < getRows(); i++) {
  for (int j = 0; j < getColumns(); j++) {
    value = getElement(i, j) * scalar;
    mat1[i][j] = value;
  }
}

return newMatrix;

这很棒,因为您[i][j]的作业已在mat1上。但看看你在plus !!

中做了些什么
FMatrix newMatrix = new FMatrix(matrix);
for (int i = 0; i < newRows; i++) {
  for (int j = 0; j < newColumns; j++) {
    value = getElement(i, j) + other.getElement(i, j);
    this.matrix[i][j] = value;
  }
}
return newMatrix;

哦,亲爱的,你正在改变this.matrix[i][j] - 你的班级不再是不可改变的。但更重要的是,你返回的价值也被破坏了。所以我相信你只需修复这个bug,你的类实际上就是不可变的。

其他错误:

  • 您永远不会从构造函数中分配值。您需要将参数中的值复制到内部数组中。

副本是必要的,所以你不能这样做

int[][] matrix = new int[3][3];
FMatrix f = new FMatrix(matrix);
matrix[1][1] = 42; // broken immutability

现在它不是问题,因为你从不将参数中的值分配给矩阵......所以不用担心。

public FMatrix(int[][] matrix) {
  if (matrix.length == 0) {
    rows = 0;
    columns = 0;
  } else {
    rows = matrix.length;
    columns = matrix[0].length;
  }
  this.matrix = new int[rows][0];
  for(int row = 0; row < rows; row++) {
      this.matrix[rows] = matrix[rows].clone();
  }
}