http://leepoint.net/notes-java/data/expressions/22compareobjects.html

事实证明,限定等于() 不是微不足道的;事实上,它是适度 难以得到它的权利,特别是在 这种情况下的子类。最好的 处理的问题是在 Horstmann的核心Java Vol.1.

如果等于()必须始终被复盖,那么什么是一个好办法不是走投无路的入具有做的对象比较?什么是一些良好的"设计"替代方案?

编辑:

我不确定这是未来的方式,我有意图。也许这个问题应该更多沿线的"为什么你想来比较两个对象?" 根据你的问题的答案,是有一个替代解决方案的比较?我的意思不是,不同的执行平等的。我的意思是,使用的不平等。我认为关键的一点是要开始这个问题,为什么你想来比较两个对象。

有帮助吗?

解决方案

如果必须始终覆盖Equals(),那么不得不进行对象比较的好方法是什么?

你误会了。您应该尽可能少地覆盖等于。


所有这些信息都来自 有效的 Java,第二版 (乔什·布洛赫)。有关此内容的第一版章节仍然免费提供 下载.

来自《有效的 Java》:

避免问题的最简单方法不是要覆盖等值方法,在这种情况下,类的每个实例都等于自身。

任意重写 equals/hashCode 的问题是继承。一些 equals 实现主张像这样测试它:

if (this.getClass() != other.getClass()) {
    return false; //inequal
}

事实上, (3.4) 当您使用源工具生成方法时,Java 编辑器会执行此操作。布洛赫认为,这是一个错误,因为它违反了 里氏替换原则.

来自《有效的 Java》:

在保留平等合同的同时,没有办法扩展一个即将的类并添加值组件。

中描述了最小化平等问题的两种方法 类和接口 章节:

  1. 优先考虑组合而不是继承
  2. 继承的设计和文档,否则禁止继承

据我所知,唯一的选择是以类外部的形式测试相等性,而执行方式将取决于类型的设计以及您尝试使用它的上下文。

例如,您可以定义一个接口来记录如何进行比较。在下面的代码中,Service 实例可能在运行时被同一类的较新版本替换 - 在这种情况下,具有不同的 ClassLoader,equals 比较将始终返回 false,因此覆盖 equals/hashCode 将是多余的。

public class Services {

    private static Map<String, Service> SERVICES = new HashMap<String, Service>();

    static interface Service {
        /** Services with the same name are considered equivalent */
        public String getName();
    }

    public static synchronized void installService(Service service) {
        SERVICES.put(service.getName(), service);
    }

    public static synchronized Service lookup(String name) {
        return SERVICES.get(name);
    }
}

“你为什么要比较两个物体?”

一个明显的例子是测试两个字符串是否相同(或者两个字符串是否相同) 文件, , 或者 URI)。例如,如果您想构建一组要解析的文件该怎么办?根据定义,该集合仅包含唯一元素。爪哇的 type 依赖 equals/hashCode 方法来强制其元素的唯一性。

其他提示

我不认为这是真的,平等应该始终被覆盖。据我了解规则是压倒一切等于只有在你对如何定义语义等价的对象明确的情况下才有意义。在这种情况下,你重写了hashCode(),以及让你没有,你已经定义为相当于返回不同的散列码的对象。

如果您不能定义有意义的等价,我看不到好处。

怎么样就去做吧?

下面是我的模板,等于它是由乔希布洛赫从有效的Java应用的知识。阅读本书了解更多详情:

@Override
public boolean equals(Object obj) {
    if(this == obj) {
        return true;
    }

    // only do this if you are a subclass and care about equals of parent
    if(!super.equals(obj)) {
        return false;
    }
    if(obj == null || getClass() != obj.getClass()) {
        return false;
    }
    final YourTypeHere other = (YourTypeHere) obj;
    if(!instanceMember1.equals(other.instanceMember1)) {
       return false;
     }
     ... rest of instanceMembers in same pattern as above....
     return true;
 }

Mmhh

在某些情况下可以使对象不可修改(read-only)和有创建的,从一个点(一个工厂的方法)

如果两个物体相同的输入数据(建立参数)需要该工厂将返回同一个实例参考和随后的使用"=="就足够了。

这种方法是有用的,在某些情况下,只。和大多数时间将会看起来矫枉过正。

看看 这个答案 要知道如何实现这样的事情。

警告,这是一个很大的代码

短怎么看的包装类的工作 由于java1.5

Integer a = Integer.valueOf( 2 );
Integer b = Integer.valueOf( 2 );

a == b 

是真的的话

new Integer( 2 ) == new Integer( 2 )  

是错误的。

它境内保持准和返回的,如果输入的价值是相同的。

你知道整数是只读的

类似的事情发生的串类这一问题。

也许我没有抓住重点,但使用 equals 而不是定义自己的方法的唯一原因 用不同的名字 是因为许多集合(可能还有 JDK 中的其他东西或现在的任何名称)期望 equals 方法定义一致的结果。但除此之外,我可以想到您想要在 equals 中进行的三种比较:

  1. 这两个对象确实是同一个实例。使用 equals 是没有意义的,因为您可以使用 ==。另外,如果我忘记了它在 Java 中的工作原理,请纠正我,默认的 equals 方法使用自动生成的哈希码来执行此操作。
  2. 这两个对象引用相同的实例,但不是同一个实例。这很有用,呃,有时...特别是如果它们是持久对象并引用数据库中的同一对象。您必须定义 equals 方法才能执行此操作。
  3. 这两个对象引用了值相等的对象,尽管它们可能是也可能不是相同的实例(换句话说,您在整个层次结构中比较值)。

为什么要比较两个对象?好吧,如果它们相等,你会想做一件事,如果不相等,你会想做另一件事。

也就是说,这取决于当前的情况。

的主要原因重载equals()在大多数情况下是检查特定类别内重复。例如,如果你想使用一个设置为包含您所创建你需要你的对象中重写equals()和hashCode()的对象。如果你想用你的自定义对象为在地图的关键同样适用。

这是至关重要的,因为我见过很多人犯错误中增加设置或地图的自定义对象,而忽略equals()和hashCode()的做法。这可能是特别阴险的原因是编译器不会抱怨,你可以包含相同的数据,但有一个集合,不允许重复引用不同的多个对象结束。

例如,如果你有一个简单的bean称为NameBean与单个字符串属性“名称”,则可以构建NameBean的两个实例(例如NAME1和NAME2),每个具有相同的“name”属性的值(例如“爱丽丝” )。然后,您可以既NAME 1和NAME新增到一组和该组将大小为2,而不是大小为1这是何意。同样,如果你有一个地图,例如地图,以名义豆到一些其他对象映射,你首先映射名1至字符串“第一”,后来被映射名2到串“第二”,你将有两个键/值对在地图(例如,NAME1 - > “第一”,NAME2 - > “第二”)。所以,当你做一个地图查找它将返回映射到您传递准确的参考,这要么是1,名称,或其他名称为“爱丽丝”将返回null参考价值。

下面是通过运行它的输出之前的具体示例:

输出:

Adding duplicates to a map (bad):
Result of map.get(bean1):first
Result of map.get(bean2):second
Result of map.get(new NameBean("Alice"): null

Adding duplicates to a map (good):
Result of map.get(bean1):second
Result of map.get(bean2):second
Result of map.get(new ImprovedNameBean("Alice"): second

代码:

// This bean cannot safely be used as a key in a Map
public class NameBean {
    private String name;
    public NameBean() {
    }
    public NameBean(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return name;
    }
}

// This bean can safely be used as a key in a Map
public class ImprovedNameBean extends NameBean {
    public ImprovedNameBean(String name) {
        super(name);
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if(obj == null || getClass() != obj.getClass()) {
            return false;
        }
        return this.getName().equals(((ImprovedNameBean)obj).getName());
    }
    @Override
    public int hashCode() {
        return getName().hashCode();
    }
}

public class MapDuplicateTest {
    public static void main(String[] args) {
        MapDuplicateTest test = new MapDuplicateTest();
        System.out.println("Adding duplicates to a map (bad):");
        test.withDuplicates();
        System.out.println("\nAdding duplicates to a map (good):");
        test.withoutDuplicates();
    }
    public void withDuplicates() {
        NameBean bean1 = new NameBean("Alice");
        NameBean bean2 = new NameBean("Alice");

        java.util.Map<NameBean, String> map
                = new java.util.HashMap<NameBean, String>();
        map.put(bean1, "first");
        map.put(bean2, "second");
        System.out.println("Result of map.get(bean1):"+map.get(bean1));
        System.out.println("Result of map.get(bean2):"+map.get(bean2));
        System.out.println("Result of map.get(new NameBean(\"Alice\"): "
                + map.get(new NameBean("Alice")));
    }
    public void withoutDuplicates() {
        ImprovedNameBean bean1 = new ImprovedNameBean("Alice");
        ImprovedNameBean bean2 = new ImprovedNameBean("Alice");

        java.util.Map<ImprovedNameBean, String> map
                = new java.util.HashMap<ImprovedNameBean, String>();
        map.put(bean1, "first");
        map.put(bean2, "second");
        System.out.println("Result of map.get(bean1):"+map.get(bean1));
        System.out.println("Result of map.get(bean2):"+map.get(bean2));
        System.out.println("Result of map.get(new ImprovedNameBean(\"Alice\"): "
                + map.get(new ImprovedNameBean("Alice")));
    }
}

平等是基本的逻辑(见的法律身份),并没有太多的编程能没有它。作为比较的类的实例编写的,很好,给你。如果你需要能够找到它们在收集或使用它们作为钥匙在地图,则需要平等的检查。

如果你写了超过几个平凡库中爪哇,你会知道,平等是难以得到的权利,特别是在工具的胸部 equalshashCode.平等结束正紧密结合起类层次结构,这使得脆代码。更重要的是,没有类型检查中提供,因为这些方法只是采取参数类型的对象。

有一种方法使平等检查(并且散列)很多不容易出错以及更多的类型安全的。在 功能Java 图书馆,你会发现 Equal<A> (和相应的 Hash<A>)在平等的是相互独立成一个单一类。它为撰写方法 Equal 实例类从现有的实例,以及包装收藏, 可迭代对象, 哈希, , , 使用 Equal<A>Hash<A> 而不是的 equalshashCode.

什么是最好对这种做法是,你可以永远不要忘记写平等和哈希方法时,他们被称为。这类系统会帮你记得。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top