Java 除了不可变的值对象之外,我什么时候应该重写'equals()`?

Java 除了不可变的值对象之外,我什么时候应该重写'equals()`?,java,equals,Java,Equals,很明显,equals()(当然还有hashCode())在处理不可变值对象时很有价值——映射键、需要跨包含它们的对象进行比较的强类型字段值等等 但是,除了值对象之外,您有两个独立构造的实例并希望它们相等的可能性有多大 对于我来说,很难想象一个现实的场景,在这个场景中,引用平等实际上不会让你得到你想要的东西;在这些特定的情况下,场景特定的等价方法(isEquivalentTo(Foo)而不是equals(Object))似乎更安全。特别是对于可变对象 在非值类型中,equals()的用例是什么Se

很明显,
equals()
(当然还有
hashCode()
)在处理不可变值对象时很有价值——映射键、需要跨包含它们的对象进行比较的强类型字段值等等

但是,除了值对象之外,您有两个独立构造的实例并希望它们
相等的可能性有多大

对于我来说,很难想象一个现实的场景,在这个场景中,引用平等实际上不会让你得到你想要的东西;在这些特定的情况下,场景特定的等价方法(
isEquivalentTo(Foo)
而不是
equals(Object)
)似乎更安全。特别是对于可变对象

在非值类型中,
equals()
的用例是什么
Set
是可变的,并且具有
equals()
的(有用的)定义。这似乎没什么用…

来自:

接口列表

布尔包含(对象o)

更正式地说,当且仅当此列表至少包含一个 元素e使得 (o==null?e==null:o.equals(e)

因此,重写
equals
对于这种(和其他)语言构造非常有用

考虑一下(对不起,这是C#):

类MimeType
{
公共字符串名称{get;set;}
公共字符串扩展名{get;set;}
公共覆盖布尔等于(对象对象对象)
{
var other=作为模拟类型的obj;
if(other==null)返回false;
返回other.Name==Name;
}
}
班级计划
{
void Main()
{
var jpeg1=新的MimeType();
jpeg1.Name=“image/jpeg”;
jpeg1.Extension=“.jpg”;
var jpeg2=新的MimeType();
jpeg2.Name=“image/jpeg”;
jpeg2.Extension=“.jpeg”;
var list=新列表();
列表.添加(jpeg1);
如果(!list.Contains(jpeg2))
列表.添加(jpeg2);
}
}
在上面的示例中,
jpeg2
对象将不会添加到列表中,因为该列表已包含等效实例

更新:


只是为了好玩,我将
扩展
成员添加到
MimeType
类中。

集合相等方法有点有用。我时不时地使用它们——通常是在我懒于单元测试断言时。但并不经常。它们不如
containsAll()
这样的东西有用,我也不清楚它们为什么是个好主意。首先,当集合是不可变的时——通过(小心地)使用
集合。不可修改集合
,或者通过番石榴的
不可变集合
,或者诸如此类——你可以将它们用作地图中的键,或者你喜欢的任何东西。公平的说,大多数情况下,为可变类型提供一个
equals()
方法并不是特别有用,但是……如果您需要测试等价性,那么,为什么不直接重写
equals
而不是创建您自己的
isEquivalentTo
方法呢?对于一个不可变类,重写
equals()并没有害处
(我可以看出它对于不可变集合框架中的子列表或子树共享是多么有用)。对于一个可变类,
isEquivalentTo()
不会让你遇到其他类或标准库的任何陷阱——比如集合框架——假设
equals()
hashCode()
是稳定的。显然,允许将例如
Set
用作映射键的有用性超过了风险的决定。我认为这是一个正确的决定。例如,使用技术上可变的映射键是非常罕见的,因此这不是一个重大风险。是的,这将是一个值类型的示例。我当然希望它是不变的!:)我更新了我的小例子。添加扩展是否会使MimeType类成为可变类型?这并不是说它们仍然是等价的,但我可以想象一些情况下,仍然需要考虑这两个实例是相等的。这就是我要问的:你能想到的那些情况是什么?我有时在基类中重写它们,并使它们成为代码>最终< /代码>,以确保所有类型的类具有与<代码>对象相同的语义。
Object.hashCode()
。这并不是你要问的:-)@Raedwald,所以基本上你要说的是,你把它们定为最后一个,以确保以后不会有其他人出现并覆盖
对象的默认行为?:)我支持这种防御性编程。:)很可能没有一个(至少没有一个真正重要的)。但是,您如何重新设计Java的核心对象层次结构以反映
equals()
对于特定类别的对象应该是
final
,但对于互补类别则可以重写?这是一个好问题。我不是一个语言设计师,但我的第一个冲动是说解决方案将与使不变性成为顶级语言概念有关,并分离身份和状态的概念(例如Clojure)。
Returns true if this list contains the specified element. 
class MimeType
{
    public string Name { get; set; }
    public string Extension { get; set; }    

    public override bool Equals(object obj)
    {
        var other = obj as MimeType;
        if (other == null) return false;
        return other.Name == Name;
    }
}

class Program
{
    void Main()
    {
        var jpeg1 = new MimeType();
        jpeg1.Name = "image/jpeg";
        jpeg1.Extension = ".jpg";

        var jpeg2 = new MimeType();
        jpeg2.Name = "image/jpeg";
        jpeg2.Extension = ".jpeg";    

        var list = new List<MimeType>();
        list.Add(jpeg1);

        if (!list.Contains(jpeg2))
            list.Add(jpeg2);
    }
}