This section presents a case study on designing classes for matrix operations using generic types. The addition and multiplication operations for all matrices are similar except that their element types differ. Therefore, you can design a superclass that describes the common operations shared by matrices of all types regardless of their element types, and you can define subclasses tailored to specific types of matrices. This case study gives implementations for two types: int and Rational. For the int type, the wrapper class Integer should be used to wrap an int value into an object, so that the object is passed in the methods for operations.
The class diagram is shown in Figure above. The methods addMatrix and
multiplyMatrix add and multiply two matrices of a generic type E[][]. The static method printResult displays the matrices, the operator, and their result. The methods add, multiply, and zero are abstract, because their implementations depend on the specific type of the array elements. For example, the zero() method returns 0 for the Integer type and 0/1 for the Rational type. These methods will be implemented in the subclasses in which the matrix element type is specified.
IntegerMatrix and RationalMatrix are concrete subclasses of GenericMatrix. These two classes implement the add, multiply, and zero methods defined in the GenericMatrix class.
The code below implements the GenericMatrix class. in line 3 specifies that the generic type is a subtype of Number. Three abstract methods—add, multiply, and zero—are defined in lines 5, 8, and 11. These methods are abstract because we cannot implement them without knowing the exact type of the elements. The addMaxtrix (lines 14–28) and multiplyMatrix (lines 31–52) methods implement the methods for adding and multiplying two matrices. All these methods must be nonstatic, because they use generic type E for the class. The printResult method (lines 55–78) is static because it is not tied to specific instances.
The matrix element type is a generic subtype of Number. This enables you to use an object of any subclass of Number as long as you can implement the abstract add, multiply, and zero methods in subclasses.
The addMatrix and multiplyMatrix methods (lines 14–52) are concrete methods. They are ready to use as long as the add, multiply, and zero methods are implemented in the subclasses.
The addMatrix and multiplyMatrix methods check the bounds of the matrices before performing operations. If the two matrices have incompatible bounds, the program throws an exception (lines 17, 34).
package demo;
public abstract class GenericMatrix<E extends Number> {
/** Abstract method for adding two elements of the matrices */
protected abstract E add(E o1, E o2);
/** Abstract method for multiplying two elements of the matrices */
protected abstract E multiply(E o1, E o2);
/** Abstract method for defining zero for the matrix element */
protected abstract E zero();
/** Add two matrices */
public E[][] addMatrix(E[][] matrix1, E[][] matrix2) {
// Check bounds of the two matrices
if((matrix1.length != matrix2.length) || (matrix1[0].length != matrix2[0].length)) {
throw new RuntimeException("The matrices do not have the same size");
}
E[][] result = (E[][])new Number[matrix1.length][matrix1[0].length];
// Perform addition
for(int i = 0; i < result.length; i++)
for(int j = 0; j < result[i].length; j++) {
result[i][j] = add(matrix1[i][j], matrix2[i][j]);
}
return result;
}
/**Multiply two matrices */
public E[][] multiplyMatrix(E[][] matrix1, E[][] matrix2) {
// Check bounds
if(matrix1[0].length != matrix2.length) {
throw new RuntimeException("The matrices do not have compatible size");
}
// Create result matrix
E[][] result = (E[][])new Number[matrix1.length][matrix2[0].length];
// Perform multiplication of two matrices
for(int i = 0; i < result.length; i++) {
for(int j = 0; j < result[0].length; j++) {
result[i][j] = zero();
for(int k = 0; k < matrix1[0].length; k++) {
result[i][j] = add(result[i][j], multiply(matrix1[i][k], matrix2[k][j]));
}
}
}
return result;
}
/** Print matrices, the operator, and their operation result */
public static void printResult(Number[][] m1, Number[][] m2, Number[][] m3, char op) {
for(int i = 0; i < m1.length; i++) {
for(int j = 0; j < m1[0].length; j++)
System.out.print(" " + m1[i][j]);
if(i == m1.length / 2)
System.out.print(" "+ op + " ");
else
System.out.print(" ");
for(int j = 0; j< m2.length; j++)
System.out.print(" " + m2[i][j]);
if(i == m1.length / 2)
System.out.print(" = ");
else
System.out.print(" ");
for(int j = 0; j < m3.length; j++)
System.out.print(m3[i][j] + " ");
System.out.println();
}
}
}
The code below implements the IntegerMatrix class. The class extends
GenericMatrix in line 3. After the generic instantiation, the add method in GenericMatrix is now Integer add(Integer o1, Integer o2). The add, multiply, and zero methods are implemented for Integer objects. These methods are still protected, because they are invoked only by the addMatrix and multiplyMatrix methods.
The code below implements the RationalMatrix class. The Rational class was introduced in Rational.java. Rational is a subtype of Number. The RationalMatrix class extends GenericMatrix in line 3. After the generic instantiation, the add method in GenericMatrix is now Rational add(Rational r1, Rational r2). The add, multiply, and zero methods are implemented for Rational objects. These methods are still protected, because they are invoked only by the addMatrix and multiplyMatrix methods.
The code below gives a program that creates two Integer matrices (lines 7–8) and an IntegerMatrix object (line 11), and adds and multiplies two matrices in lines 14 and 17.
The code below gives a program that creates two Rational matrices (lines 7–13) and a RationalMatrix object (line 16) and adds and multiplies two matrices in lines 19 and 22.
Top comments (0)