Javagenerics和数字

为了试图看看我是否可以清理我的一些数学代码,主要是矩阵的东西,我试图使用一些Javagenerics。 我有以下方法:

private  T[][] zeroMatrix(int row, int col) { T[][] retVal = (T[][])new Object[row][col]; for(int i = row; i < row; i++) { for(int j = col; j < col; j++) { retVal[i][j] = 0; } } return retVal; } 

行retVal [i] [j] = 0是导致我头痛的那一行。 该行的目标是使用T表示为0初始化数组。我试图用它做各种各样的事情:(T在类中定义为T extends Number)

 retVal[i][j] = (T)0; retVal[i][j] = new T(0); 

唯一有效的是

 retVal[i][j] = (T)new Object(0); 

这不是我想要的。

这可能吗? 有没有更简单的方法来表示任何类型的数字(包括可能的BigDecimal)的NxM矩阵,还是我被困?

  T[][] zeroMatrix(Class 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; } 

用法:

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

编辑

通用矩阵:

 public static  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 

数组和generics不能很好地结合在一起:

“数组是协变的,这意味着超类型引用数组是子类型引用数组的超类型。也就是说, Object[]String[]的超类型,字符串数组可以通过Object[]类型的引用变量访问Object[] 。“

请参阅Java Generics FAQ :

  • 我可以创建一个组件类型是具体参数化类型的数组吗?
  • 我如何一般创建对象和数组?

它应该为null而不是零。

如果你想实际放入对象T的等效0,你需要提供一个T的工厂。像这样:

 interface Factory { T getZero(); } 

你应该像这样制作方法:

 private  T[][] zeroMatrix(int row, int col, Factory 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; } 

您还应该为工厂正确实施:

  class IntegerFactory implements Factory { Integer getZero() { return new Integer(0); } } 

通常,您也会将getMatrix(int row, int column)放在工厂实现中,以便实际返回正确的类型化数组。

在Java中,类型在运行时被擦除,因此您需要传入另一个参数以在运行时获取类型。

这可能是初始化数组的值,或者是要使用的类。

如果您选择传入类,则将class of value设置为值,以便为每种类型存储零值。

然后,您可以使用java.util.Arrays.fill来填充数组:

 private static HashMap, Object> ZEROS = new HashMap,Object>(); static { ZEROS.put( Integer.class, Integer.valueOf(0) ); ... } private static  T[][] zeroMatrix ( Class 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); 

但是,如果远程关注性能,则不希望使用数字代码的盒装值。

你真的不想尝试将null用作零 – 它将使代码中所有其他路径的复杂性增加三倍。 虽然您可能会使用数字支持类来提供各种盒装数字类型的加法和乘法,但与提供两个或三个原始矩阵和几个大数字矩阵相比,您节省的复杂程度将非常少,特别是如果你使用模板系统(例如ant的替换任务或XSLT)来生成源代码。

Ye olde(参考)数组与generics不匹配。 在这种情况下,arrays也可能效率低下。 您正在创建一个数组数组,因此存在不必要的间接和边界检查。 最好制作一个Matrix类。 您可能还想向Matrix添加对表示零的T实例的引用。

generics和数组不匹配。 不允许创建通用数组,因为它不是类型安全的。 它源于如果Sub是Super的子类型,那么Sub []是Super []的子类型,而不是generics类型的情况; 对于任何两种不同类型Type1和Type2,List既不是List的子类型,也不是List的超类型。 (Effective Java在第5章第25项中介绍了这一点)。

我想你正在打一场失败的战斗。 即使你解决了这个问题,你又如何计划解决加法,减法等问题呢? 数字类不是一个非常有用的超类,而唯一有用的方法是doubleValue()。

零可以被定义为加法中的标识或乘法中的零,但是没有加法或乘法的通用定义,零的通用定义是不可能的。

如果你想要这个,那么你可能会更好地坚持使用BigDecimal来做所有事情,但当然这会产生相关的性能损失。

另一个明显的选择是将数组保留为null初始化,然后更改其他代码以将null视为零。

如果你真的想使用generics,你可以做这样的事情

 private  T[][] zeroMatrix(int row, int col, Class clazz) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { T[][] retVal = (T[][]) Array.newInstance(clazz, new int[] { row, col }); for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { Constructor c = clazz.getDeclaredConstructors()[0]; retVal[i][j] = c.newInstance("0"); } } return retVal; } 

例:

 zeroMatrix(12, 12, Integer.class); 

您需要考虑generics仅在编译时用于类型安全检查。 此信息在运行时丢失,因此您无法在retVal [i] [j] = 0上使用自动装箱; 因为Java不能自动为“类型”或“对象”键入框。

如果传入要设置的值,它将起作用。 这是一个快速示例:

 private  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; } 

顺便说一句,(int i = row; i

编辑:您将无法将结果转换为Object [] []以外的其他内容,因为这是实际的数组类型。

我提出了一个相关问题,该问题还询问了引用您问题的性能问题 。 一致认为,对Generics的重构有相当大的性能影响,因此如果这很重要,你应该坚持使用原语(对我而言)。

Java使用擦除来实现generics意味着你将无法使用generics类型。

如何使用null来表示0

 retVal[i][j] = null; 

然后,您可以稍后将所需的任何类型分配给arrays。