为什么Java中的异常类不实现equals方法?

5

为什么Exception类(例如RuntimeException)没有实现equals()方法?

例如,这段代码的输出false

/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        final RuntimeException e1 = new RuntimeException("e");
        final RuntimeException e2 = new RuntimeException("e");
        System.out.println(e1.equals(e2));
    }
}

是否有特殊原因未实现equals()方法?看起来这个方法非常有用。

编辑:让我详细解释一下问题,因为似乎大多数人认为equals()的实现没有用处。

我想知道,为什么异常应该与普通对象不同对待,只有在相同实例时才匹配。难道它没有足够的信息来与其他对象进行比较吗?例如,底层的StackTraceElement,我假设它存储调用堆栈,已经实现了一个equals方法。

在异常的定义中是否有阻止它们基于其属性实现等效方法的内容?


10
为什么需要比较“异常(Exception)”?我从来没有需要比较“异常(Exception)”。 - rgettman
2
如果您想比较 RuntimeException 对象,请创建一个自己的子类,重写 equals 方法。 - deyur
14
这些异常会有不同的堆栈跟踪信息,那么你真的认为它们是相等的吗? - John Kugelman
@asteri,你能详细解释一下“根据定义,必须在不同的堆栈帧中声明”的含义吗? - SANDeveloper
就使用情况而言,我有一段复杂的代码,它将异常存储在一个映射中。我正在尝试比较两个映射的内容。我可以明显地解包映射并仅检查异常实例,但这让我想知道为什么默认情况下实现是缺失的。 - SANDeveloper
显示剩余8条评论
3个回答

5
简单来说,两个不同的异常实例在任何意义上都不相等。那么什么是异常的“相等”呢?如果两个对象相等,它们在某种程度上是语义上相同的。但是,两个Exception实例始终是语义上不同的——要么异常发生在另一个地方(即堆栈跟踪不同),要么最可能的是,在另一个时间、也就是在不同的情况下发生了异常。两个不同的Exception实例永远不应该相等,因为它们不能代表相同的事情。

3
请注意,虽然 Exception 没有重写 equals 方法,但是从 Object 继承的实现比较的是对象的身份。

对于大多数情况下,您可以实现一个异常比较器,例如仅记录第一次出现的异常,或者与模板进行比较,尽管我猜堆栈跟踪在这种情况下不可比较。如果想支持更多基本字段之外的某些异常类,则必须为它们定义特定的比较器。

异常并不太适合使用值语义。如果它们具有值语义,您会怎么做?将它们用作哈希表键?在这种情况下,您可以实现一个包装类,使其 equalshashCode 的行为像这样。


2

异常是普通的类实例,可以维护状态。

例如,一个人可以声称,在特定行抛出一个带特定消息的异常与在下一行抛出的异常不相等。

即使错误在同一行上抛出并且具有相同的调用堆栈,但是程序的不同运行可以使异常不同...例如,第一个程序可以用以下命令调用:

java -jar program.jar "arg1" & java -jar program.jar "arg2"

这些程序有不同的pid,在不同的时间戳被抛出,...。
但是你可以通过继承(对于自己定义的Exception)来实现equals方法:。
public class WeirdException extends Exception {

    @Override
    public boolean equals (Object obj) {
        return (obj instanceof WeirdException);
    }

}

毕竟,堆栈跟踪仅在构造时存储在“异常”中,以下是一个完全有效的Java程序:
public class MyClass {


    public static void main(String args[]){
        Exception ex = new IndexOutOfBoundsException();
        ex.printStackTrace(System.out);
    }
}

结果如下:
java.lang.IndexOutOfBoundsException
    at MyClass.main(MyClass.java:15)

换句话说,throwException 没有太多关系。 throw 只是意味着一种“替代的 return”。它与存储堆栈跟踪没有任何关系,...

那不是一个相等测试,而只是一个测试实例。 - Chris Stratton
@ChrisStratton:如果异常不包含状态,那就差不多了。但第一个需要问的问题(这在答案中有记录)是,当两个“Exception”相等时,它们是否需要共享相同的堆栈跟踪?相同的进程?相同的时间戳... - Willem Van Onsem
我能理解你的意思。但是在第一行,你是否意味着异常不是普通的类实例? - SANDeveloper
@SANDeveloper:不,ExceptionArrayList一样,都是类。只有在构造Exception时(举个例子),在构造函数级别上,堆栈跟踪才会被序列化并存储在异常中等等。 - Willem Van Onsem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接