In an attempt to see if I can clean up some of my math code, mostly matrix stuff, I am trying to use some Java Generics. I have the following method:
private <
You need to consider that generics are only used at compile-time for type safety checking. This information is lost at runtime so you can't use auto-boxing on retVal[i][j] = 0; since Java can't auto-box to type Number or Object.
If you pass in the value you want to set, it will work. Here's a quick sample:
private <T> T[][] fillMatrix(int row, int col, T value) {
T[][] retVal = (T[][])new Object[row][col];
for(int i = 0; i < row; i++) {
for(int j = 0; j < col; j++) {
retVal[i][j] = value;
}
}
return retVal;
}
Btw, for(int i = row; i < row; i++) and for(int j = col; j < col; j++) will never loop so there's another problem with your code.
edit: You won't be able to cast the result to something other than Object[][] though because that's the actual array type.
Java's use of erasure to implement generics means that you're going to have trouble new'ing a generic type.
How about using null to represent 0
retVal[i][j] = null;
You can then assign any type you want to the array later on.
Generics and arrays don't match very well. Creating a generic array is not allowed since it would not be typesafe. It stems from the fact that if Sub is a subtype of Super, then Sub[] is a subtype of Super[], which is not the case of generic types; for any two distinct types Type1 and Type2, List is neither a subtype or a supertype of List. (Effective Java covers this in chapter 5, item 25).
<T extends Number> T[][] zeroMatrix(Class<? extends Number> of, int row, int col) {
T[][] matrix = (T[][]) java.lang.reflect.Array.newInstance(of, row, col);
T zero = (T) of.getConstructor(String.class).newInstance("0");
// not handling exception
for (int i = 0; i < row; i++) {
for (int j = 0; j < col;
matrix[i][j] = zero;
}
}
return matrix;
}
usage:
BigInteger[][] bigIntegerMatrix = zeroMatrix(BigInteger.class, 3, 3);
Integer[][] integerMatrix = zeroMatrix(Integer.class, 3, 3);
Float[][] floatMatrix = zeroMatrix(Float.class, 3, 3);
String[][] error = zeroMatrix(String.class, 3, 3); // <--- compile time error
System.out.println(Arrays.deepToString(bigIntegerMatrix));
System.out.println(Arrays.deepToString(integerMatrix));
System.out.println(Arrays.deepToString(floatMatrix));
EDIT
a generic matrix:
public static <T> T[][] fillMatrix(Object fill, int row, int col) {
T[][] matrix = (T[][]) Array.newInstance(fill.getClass(), row, col);
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
matrix[i][j] = (T) fill;
}
}
return matrix;
}
Integer[][] zeroMatrix = fillMatrix(0, 3, 3); // a zero-filled 3x3 matrix
String[][] stringMatrix = fillMatrix("B", 2, 2); // a B-filled 2x2 matrix
it should be null instead of zero.
If you want to actually put in there the equivalent 0 for object T you will need to provide a factory of T. Something like this:
interface Factory<T> {
T getZero();
}
and you should make the method like this:
private <T> T[][] zeroMatrix(int row, int col, Factory<T> factory) {
T[][] retVal = (T[][])new Object[row][col];
for(int i = row; i < row; i++) {
for(int j = col; j < col; j++) {
retVal[i][j] = factory.getZero();
}
}
return retVal;
}
You should also have proper implementations for the factory:
class IntegerFactory implements Factory<Integer> {
Integer getZero() {
return new Integer(0);
}
}
Normally you would put the getMatrix(int row, int column)
in the factory implementation too in order to actually return a proper typed array.
In Java the type is erased at runtime, so you need to pass in another argument to get the type at runtime.
That could either be the value to initialise the arrays with, or the class to use.
If you choose to pass in the class, then have a Map of class to value to store a zero value for each type.
You then can use java.util.Arrays.fill
to fill the array:
private static HashMap<Class<?>, Object> ZEROS = new HashMap<Class<?>,Object>();
static {
ZEROS.put( Integer.class, Integer.valueOf(0) );
...
}
private static <T extends Number> T[][] zeroMatrix ( Class<T> type, int rows, int cols ) {
@SuppressWarnings("unchecked")
T[][] matrix = (T[][]) java.lang.reflect.Array.newInstance(type, rows, cols);
Object zero = ZEROS.get(type);
for ( T[] row : matrix )
java.util.Arrays.fill(row,zero);
return matrix;
}
Integer[][] matrix = zeroMatrix (Integer.class, 10, 10);
However, if performance is remotely a concern you don't want to be using boxed values for numeric code.
You really don't want to try and use null for zero - it will treble the complexity of all other paths in your code. Although you might get away with a numeric support class which would provide addition and multiplication of the various boxed number types, the amount of complexity you save will be very little compared with providing two or three primitive matrices and a couple of big-number ones, particularly if you use a templating system (eg ant's replace task or XSLT) to generate the source code.