我有一个矩阵类,它接受一个扩展Number的泛型对象。
例如:
public class Matrix<T extends Number>
我试图比较具有相同值的两个矩阵:
Matrix:
row=[0] 273 455
row=[1] 243 235
row=[2] 244 205
row=[3] 102 160
和
Matrix:
row=[0] 273 455
row=[1] 243 235
row=[2] 244 205
row=[3] 102 160
在Matrix类中,我有一个equals方法,如下所示:
public boolean equals(Object obj) {
if (obj == null)
return false;
if (!(obj instanceof Matrix))
return false;
Matrix<T> m = (Matrix<T>) obj;
if (this.rows != m.rows)
return false;
if (this.cols != m.cols)
return false;
for (int i=0; i<matrix.length; i++) {
T t1 = matrix[i];
T t2 = m.matrix[i];
if (!t1.equals(t2))
return false;
}
return true;
}
这一行失败了:
t1.equals(t2)
即使两个数字相等。例如“273”和“273”
当我调试equals方法时,它失败了,因为假设数字是Longs:
这是来自Java SDK Long.class:
public boolean equals(Object obj) {
if (obj instanceof Long) {
return value == ((Long)obj).longValue();
}
return false;
}
基本上,它失败了,因为obj不是Long的实例。
我可以轻松更改我的等于方法:
if (t1.longValue()!=t2.longValue())
return false;
但是我想知道在这种情况下检查相等性的正确方法是什么,以及为什么通用T上的equals方法假设它是一个Long。
编辑:
我的测试代码是定义''矩阵泛型类型的整数'',这使得相等测试(使用Long进行比较)对我来说很奇怪。
测试代码:
Matrix<Integer> matrix1 = new Matrix<Integer>(4, 3);
matrix1.set(0, 0, 14);
matrix1.set(0, 1, 9);
matrix1.set(0, 2, 3);
matrix1.set(1, 0, 2);
matrix1.set(1, 1, 11);
matrix1.set(1, 2, 15);
matrix1.set(2, 0, 0);
matrix1.set(2, 1, 12);
matrix1.set(2, 2, 17);
matrix1.set(3, 0, 5);
matrix1.set(3, 1, 2);
matrix1.set(3, 2, 3);
Matrix<Integer> matrix2 = new Matrix<Integer>(3, 2);
matrix2.set(0, 0, 12);
matrix2.set(0, 1, 25);
matrix2.set(1, 0, 9);
matrix2.set(1, 1, 10);
matrix2.set(2, 0, 8);
matrix2.set(2, 1, 5);
Matrix<Integer> result1 = new Matrix<Integer>(4,2);
result1.set(0, 0, 273);
result1.set(0, 1, 455);
result1.set(1, 0, 243);
result1.set(1, 1, 235);
result1.set(2, 0, 244);
result1.set(2, 1, 205);
result1.set(3, 0, 102);
result1.set(3, 1, 160);
Matrix<Integer> matrix3 = matrix1.multiply(matrix2);
if (!matrix3.equals(result1)) {
System.err.println("Matrix multiplication error. matrix3="+matrix3+" result1"+result1);
return false;
}
Here是没有定义equals()方法的Matrix代码的链接。我还没有检查过equals()代码。
答案 0 :(得分:3)
即使您的所有测试代码都使用Long.equals
,该程序使用Matrix<Integer>
的原因只是您在其中存储了Long
。代码有:
public class Matrix<T extends Number> {
private T[] matrix = null;
然后在构造函数中:
this.matrix = (T[]) new Number[rows * cols];
当然会创建一个null
引用数组。但是当您使用multiply
创建数组时,
Long result = 0l;
for (int i = 0; i < cols; i++) {
Long l = row[i].longValue() * column[i].longValue();
result += l;
}
output.set(r, c, (T) result);
set
看起来像
public void set(int row, int col, T value) {
matrix[getIndex(row, col)] = value;
}
问题是,即使你试图对结果进行(T)
强制转换,它也没有做任何事情。请注意,该语言不允许您在Long
和Integer
类型之间进行投射:
Long x = 3L;
Integer y = 4;
x = (Long)y; // illegal
y = (Integer)x; // illegal
由于类型擦除,编译器不会尝试检查强制转换为(T)
,但如果您不禁止警告,则会显示未经检查的警告。 (它会检查以确保您投射的内容有些Number
,但这就是全部。)不生成任何可进行转换的代码。因此,即使在Matrix<Integer>
上调用乘法方法,代码也不会尝试将Long
转换为Integer
。强制转换无效,结果是Long
数组中存储了对matrix
的引用。 (这样运行正常,因为代码无法检查以确保类型与T
相同,这也是由于类型擦除。)因此,稍后,当您使用matrix[i].equals
时,因为{{1存储在Long
中,matrix
被调用。
不幸的是,我认为将数字转换为某些Long.equals
类的对象是一种很好的方法,这种对象在编译时是不可知的。您可以将Number
的类作为参数传递给构造函数,然后使用反射来尝试查找带有T
参数的该类的构造函数,但是这样做了难看。
答案 1 :(得分:2)
不幸的是,没有办法检查Number实现的值是否相等。
您通常可以做的最接近的事情是:
public static boolean valueEquals(Number n1, Number n2) {
return n1.longValue() == n2.longValue() && n1.doubleValue() == n2.doubleValue();
}
此方法将返回所有原始包装类型的合理结果,但不会返回BigDecimal,BigInteger和其他任何提供超出long / double限制的精度的实例。 比较longValue和doubleValue的原因是,如果你只使用longValue,1L和1.1D将被检测为相等(由于Double.longValue()中的截断),而如果你只使用doubleValue,则会有多个不同的长匹配单个double的值(例如,2 ^ 53到2 ^ 63范围内的整数值可以精确地表示为long,但是当表示为double时,它们会在最低有效位中进行舍入)。
答案 2 :(得分:1)
由于您已经知道这些值并不总是Long
的实例,因此您必须自己比较数值。
回答你的问题“我想知道为什么通用T上的equals方法假设它很长”:
因为它是一个。在t1.equals(t2)
为长整数的情况下调用t1
时,相等检查由Long.equals(Object)
完成。如果参数的类型不同,则会导致错误。
由于您无法确定equals方法中哪种类型“到达”,因此您应该实现一个可以处理所有可能类型的Comparator。例如:你如何比较整数和双数?两者都是Number的子类。
答案 3 :(得分:-1)
...为什么通用T上的equals方法假设它是Long。
原因很简单:假设您正在测试的矩阵属于Matrix<Long>
类型,那么t1
是 Long
的实例(通用type只允许你在这里使用Long
并且在运行时没有相关性,因此会调用Long.equals()
。
在下列情况下,应调用Integer.equals()
:
Matrix<Integer> m1 = ...;
Matrix<Long> m2 = ...;
m1.equals( m2 );
由于m1的成员属于Integer类型,因此调用t1.equals(t2)
将具有签名Integer.equals(Long)
。
那么你能做些什么才能得到两个不同类型的矩阵,但是它们的值相等?
一般问题是你应该使用compareTo()
来检查价值相等性(因为在某些情况下像BigDecimal
这样在数学上相等的值(如2.0和2.00)不会导致equals()
回归真。
不幸的是,使用T extends Number & Comparable<T>
不是一个选项(请参阅评论以供参考,此处:Why doesn't java.lang.Number implement Comparable?),因为您无法以这种方式调用Long.compareTo(Integer)
。
因此,您必须回退到原始值并区分整数和浮点值(从而调用t1.longValue()
或 t1.doubleValue()
)或使用{{ 1}} Comparator<Number>
的实现会处理它。 (应该准备好使用compareTo(Number lhs, Number rhs)
,如下所示:http://www.jidesoft.com/javadoc/com/jidesoft/comparator/NumberComparator.html)。
如果您想支持更大的数字,例如Comparators
和BigInteger
,您还可以考虑使用BigDecimal
并为每个值创建一个实例。这应该会带来一些灵活性,但也会产生一些性能成本。 (免责声明:这只是一个未经考验的想法,所以不要只是按原样,它只是为你自己的思维过程提供一些输入。)