比较“T extends Number”是否相等

时间:2014-06-12 15:19:28

标签: java generics

我有一个矩阵类,它接受一个扩展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()代码。

4 个答案:

答案 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)强制转换,它也没有做任何事情。请注意,该语言不允许您在LongInteger类型之间进行投射:

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

如果您想支持更大的数字,例如ComparatorsBigInteger,您还可以考虑使用BigDecimal并为每个值创建一个实例。这应该会带来一些灵活性,但也会产生一些性能成本。 (免责声明:这只是一个未经考验的想法,所以不要只是按原样,它只是为你自己的思维过程提供一些输入。)