20

我正在尝试覆盖equals参数化类的方法。

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (!(obj instanceof Tuple))
        return false;

    Tuple<E> other = (Tuple<E>) obj; //unchecked cast
    if (!a0.equals(other.a0) && !a0.equals(other.a1)) {
        return false;
    }
    if (!a1.equals(other.a1) && !a1.equals(other.a0)) {
        return false;
    }

    return true;
}

我怎样才能确保对象的<E>那个other是一样的this

4

9 回答 9

16

您可以通过保留对Class<E>类型的引用来做到这一点。但是,在我看来,相等性测试应该是关于对象所代表的值,而不是值所表达的具体类型

一个典型的例子就是 Collections API。 new ArrayList<String>().equals(new LinkedList<Object>())返回true。虽然它们具有完全不同的类型,但它们代表相同的值,即“空集合”。

就个人而言,Tuple表示相同数据(例如("a", "b"))的两个 s 是否应该不相等,因为一个是 typeTuple<String>而另一个是Tuple<Object>

于 2009-10-27T04:56:51.830 回答
5

因为擦除你不能。您能做的最好的事情是在元组类中存储您计划让元组保存在“java.lang.Class”字段成员中的类型。然后您可以比较这些字段以确保元组类具有相同的类型。

另请参阅此线程: Java 中 C++ Pair<L,R> 的等价物是什么?

如果您发布更多关于您的课程的信息,将会有所帮助。我认为未经检查的演员表和您等同的字段数意味着它应该是 Tuple<E,F> 不?

编辑:这是我经常使用的一个有用的 Pair 类(如果需要,你可以调整你的 Tuple 类)。注意,类似于其他人的建议,这个类只是让包含的成员决定平等问题。您的用例应该确定相等性是否真的基于所包含成员的类型。

/**
 * Adapted from http://forums.sun.com/thread.jspa?threadID=5132045
 * 
 * 
 * @author Tim Harsch
 *
 * @param <L>
 * @param <R>
 */
public class Pair<L, R> {

    private final L left;
    private final R right;

    public R getRight() {
        return right;
    } // end getter

    public L getLeft() {
        return left;
    } // end getter

    public Pair(final L left, final R right) {
        this.left = left;
        this.right = right;
    } // end constructor

    public static <A, B> Pair<A, B> create(A left, B right) {
        return new Pair<A, B>(left, right);
    } // end factory method

    @Override
    public final boolean equals(Object o) {
        if (!(o instanceof Pair<?,?>))
            return false;

        final Pair<?, ?> other = (Pair<?, ?>) o;
        return equal(getLeft(), other.getLeft()) && equal(getRight(), other.getRight());
    } // end method

    public static final boolean equal(Object o1, Object o2) {
        if (o1 == null) {
            return o2 == null;
        }
        return o1.equals(o2);
    } // end method

    @Override
    public int hashCode() {
        int hLeft = getLeft() == null ? 0 : getLeft().hashCode();
        int hRight = getRight() == null ? 0 : getRight().hashCode();

        return hLeft + (37 * hRight);
    } // end method

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append('<');
        if( left == null ) {
            sb.append("null");
        } else {
            sb.append(left.toString());
        } // end if
        sb.append(',');
        if( right == null ) {
            sb.append("null");
        } else {
            sb.append(right.toString());
        } // end if
        sb.append('>');
        return sb.toString();
    } // end method
} // end class
于 2009-10-27T04:12:53.800 回答
5

我自己也遇到了这个问题,在我的特殊情况下,我不需要知道 E 型。

例如:

public class Example<E> {
    E value;

    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Example<?> other = (Example<?>) obj;
        if (value == null) {
            if (other.value != null)
                return false;
        } else if (!value.equals(other.value))
            return false;
        return true;
    }
}

在上面的代码中,由于使用Example<?>. 类型参数通配符'?' 节省一天。

于 2011-12-22T23:59:18.710 回答
3

由于泛型在编译时被删除,你基本上不能。在运行时,任何类型参数都消失了,就 JVM 而言,它们在所有方面都完全相同。

解决这个问题的方法是存储一个Class表示类型的字段,并在构造函数中创建具有该类型的对象。

示例构造函数:

public class Tuple < E > {

    public Tuple(Class<E> c) {
        //store the class
    }

}

或者你可以使用工厂:

public static <E> Tuple <E> getTuple(Class<E> type) {
    // Create and return the tuple, 
    // just store that type variable in it 
    // for future use in equals.
}
于 2009-10-27T04:13:47.777 回答
3

可悲的是,您不能在编译时执行此操作;信息不见了。这就是类型擦除的后果。另一种方法是将参数存储为Class实例,然后再查找。

于 2009-10-27T04:13:54.563 回答
2

使用对象保留对E' 类型的引用的建议Class似乎效率低下Class(这是对占用内存的大量无意义的引用)并且对您的问题毫无意义。

这通常不是真的,Foo<Bar>应该Foo<Baz>是不平等的。所以你不需要E. 而且,在您编写的代码中,您甚至不需要它来编译。只需转换为Tuple<?>,因为那是您当时所知道的全部Tuple。它仍然可以编译。

如果传递给您的Tuples 包含两种截然不同的类型的数据,则这些元素将不是equals,并且您的方法将false根据需要返回 。(一个希望 - 取决于那些健全实施的类型equals。)

于 2009-10-27T10:37:51.043 回答
0

使用反射。http://tutorials.jenkov.com/java-reflection/generics.html

于 2009-10-27T04:10:45.583 回答
0

题外话 - 您是否意识到根据您的实现,Tuple(a0, a1) 等于 Tuple(a1, a1)?我怀疑这不是你想要的...

正如其他人所说,在主题上,擦除使这成为不可能。但是您应该重新考虑为什么要这样做-相等检查仅在运行时发生,而泛型仅在 compile-time。从概念上讲,变量具有通用参数,但对象没有。因此,当您比较对象的相等性时,泛型参数根本无关紧要。无论如何,您都无法在运行时基于它们采取任何适当的措施。

对象相等和通用参数协方差/逆变,是两个正交的问题。

于 2009-10-27T10:46:05.540 回答
0

我同意上面的评论,为什么E类需要相等?你想如何处理 E 的子类?

无论如何,鉴于这是一个可以帮助您的代码示例:

public class Example<T> {

  T t;

  public Example(T t) {
    this.t = t;
  }

  public static void main(String[] args) {
    final String s = "string";
    final Integer i = 1;
    final Number n = 1;

    final Example<String> exampleString = new Example<String>(s);
    final Example<Integer> exampleInteger = new Example<Integer>(i);
    final Example<Number> exampleNumber = new Example<Number>(n);

    System.out.println("exampleString subclass "  + exampleString.t.getClass());
    System.out.println("exmapleIntger subclass " + exampleInteger.t.getClass());
    System.out.println("exmapleNumber subclass " + exampleNumber.t.getClass());
    System.out.println("Integer equals Number = " + 
        exampleInteger.t.equals(exampleNumber.t));
  }
}

您可以调用 t.getClass() 来获取有关 T 类型的类信息(当然,假设它不为空。)

我希望这有帮助。

于 2009-10-27T14:55:50.167 回答