Как правильно работать с полями при создании интерфейса?
У меня есть интерфейс IMatrix
:
public interface IMatrix {
int[][] data = null;
public int rows = 0;
public int cols = 0;
int getElement(int row, int column);
void setElement(int row, int column, int value);
IMatrix sum(IMatrix m1, IMatrix m2);
IMatrix product(IMatrix other);
boolean equals(IMatrix other);
String toString();
}
К примеру, здесь есть метод sum()
, который я хочу реализовать в каждом из классов по-разному, но при обращении к полям rows
и cols
Eclipse выдает предупреждение:
The static field IMatrix.cols should be accessed in a static way
Не совсем понимаю, как правильно реализовывать методы у классов, имплементирующих интерфейс.
Вот как я попытался реализовать этот метод в классе UsualMatrix
, реализующем IMatrix
:
@Override
public IMatrix sum(IMatrix m1, IMatrix m2) {
if ((m1.rows != m2.rows) || (m1.cols != m2.cols)) {
throw new MatrixOperationException("Matrices must be the same size to be added together.");
}
UsualMatrix mtxFinal = new UsualMatrix(rows, cols);
for (int i = 0; i < this.rows; i++) {
for (int j = 0; j < this.cols; j++) {
mtxFinal.data[i][j] = m1.data[i][j] + m2.data[i][j];
}
}
return mtxFinal;
}
Ответы (2 шт):
Поскольку главный вопрос сформулирован Как правильно работать с полями при создании интерфейса?, ответом может быть следующее:
- Интерфейс описывает поведение некоторого объекта, то есть классический интерфейс сводится к указанию сигнатур методов
- Поля (экземпляра) определяют параметры состояния этого объекта, и они вообще НЕ могут использоваться при создании интерфейса.
В предыдущих ответах указано, что "поля" в интерфейсе являются статическими (т.е. относятся ко всем экземплярам, реализующим данный интерфейс), но они также являются финальными (константными), даже если пропущен соответствующий модификатор final
.
Определение их в интерфейсе и присвоение им неких начальных значений бессмысленно -- для всех экземпляров, реализующих IMatrix
, data
будет всегда равно null
, а rows
, cols
соответственно 0, и это нельзя будет изменить.
То, что проявилось только предупреждение от Eclipse в реализации метода sum
, говорит лишь о том, что в классе UsualMatrix
либо была написана заглушка вместо конструктора, либо уже были добавлены аналогичные поля экземпляра, иначе возникли бы ошибки компиляции при попытке присвоить значение финальной переменной:
// class UsualMatrix
// поля экземпляра
private int rows, cols; // без этого объявления была бы попытка изменить константы в интерфейсе
private int[][] data; // аналогично
public UsualMatrix(int r, int c) {
this.rows = r;
this.cols = c;
this.data = new int[rows][cols];
}
К сожалению, Java допускает подобные омонимы при объявлении статических переменных и переменных экземпляра.
Далее, рассмотрим реализацию метода sum
.
Очевидно, при попытке выполнить этот метод будет выброшено исключение NullPointerException
при попытке обратиться напрямую к переменной data
для переменных m1
(m2
):
mtxFinal.data[i][j] = m1.data[i][j] + m2.data[i][j]; // m1.data и m2.data - null!
так как они объявлены с типом IMatrix
, в контексте которого существует только статическая константа data
, равная null
!
Исходя из вышесказанного, для "правильной" работы с полями их следует полностью убрать из интерфейса IMatrix
, например, перенести в некий базовый (абстрактный) класс, как указано в более ранних ответах @talex и @RomanC, а в интерфейсе оставить только геттеры для размеров матрицы и методы для чтения/записи элементов.
В этом же базовом / абстрактном классе следует реализовать методы класса Object
equals
(чтобы корректно вызвать перегруженный вариант equals(IMatrix that)
), соответственно, hashCode
и/или toString
.
Методы для операций над матрицей можно вынести в отдельный класс, инкапсулирующий алгоритмы работы с матрицами.
Пример реализации:
public interface IMatrix {
int rows();
int cols();
int getElement(int row, int column);
void setElement(int row, int column, int value);
}
При необходимости можно реализовать конструкторы для копирования исходного массива или обычного присваивания ссылки на массив и т.д.
public class UsualMatrix implements IMatrix {
private final int rows, cols;
private final int[][] data;
public UsualMatrix(int rows, int cols) {
this.rows = rows;
this.cols = cols;
this.data = new int[rows][cols];
}
@Override public int rows() { return this.rows; }
@Override public int cols() { return this.cols; }
@Override public int getElement(int row, int column) {
return this.data[row][column];
}
@Override public void setElement(int row, int column, int value) {
this.data[row][column] = value;
}
// TODO
// @Override public boolean equals(Object o) {...}
// @Override public int hashCode() {...} // вместе с equals
// @Override public String toString() {...}
}
В данной реализации алгоритмов обработки матриц используются только соответствующие методы интерфейса IMatrix
, без "прямого" доступа к полям экземпляра.
public class IMatrixOperations {
public static IMatrix sum(IMatrix mx1, IMatrix mx2) {
int r = mx1.rows();
int c = mx1.cols();
if((r != mx2.rows()) || (c != mx2.cols())) {
throw new IllegalArgumentException("Matrices must be the same size to be added together.");
}
IMatrix result = new UsualMatrix(r, c);
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
result.setElement(i, j, mx1.getElement(i, j) + mx2.getElement(i, j));
}
}
return result;
}
//....
}