【问题标题】:Code reuse for 'all objects of a type are equal' pattern“一种类型的所有对象都相等”模式的代码重用
【发布时间】:2012-12-05 18:39:40
【问题描述】:

我有许多类都覆盖了它们的 equals 和 hashCode 方法,如下所示:

final class MyClass {
  public void statelessMethod() {
    // ...
  }

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

  @Override
  public int hashCode() {
    return MyClass.class.hashCode();
  }
}

这个想法是,虽然类没有状态会发生变化new MyClass().equals(new MyClass()) 应该始终是正确的。

我正在寻找的是一些我可以编写(或重用)的代码模式或实用程序类,它们将尽可能多地删除样板文件。

背景

我正在使用这种模式来定义 Guice 模块,这样如果多个模块依赖于 UtilModule(例如),我最终不会遇到由同一类型的多个绑定引起的错误。

额外背景

这个模式是从 Guice Servlets InternalServletModule 复制而来的,它使用它来允许您在注入器配置中指定多个 ServletModule 模块,而无需复制作为该类的一部分提供的绑定。

真实使用示例回答部分cmets

final class UtilModule extends AbstractModule {
  public void configure() {
    // bindings and things
  }

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

  @Override
  public int hashCode() {
    return UtilModule.class.hashCode();
  }
}

多个模块的示例取决于 UtilModule(来自 cmets)

class Module1 extends AbstractModule {
  @Override
  protected void configure() {
    bind(SomeClass.class).to(SomeClassImpl.class);
    install(new UtilModule()); // needed by SomeClassImpl
  }
}

class Module2 extends AbstractModule {
  @Override
  protected void configure() {
    bind(MyService.class).in(Singleton.class);
    install(new UtilModule()); // needed by MyService
  }
}

class Main {
  public static void main(String[] args) {
    Injector injector = Guice.createInjector(
        new Module1(),
        new Module2());
    // ...
  }
}

【问题讨论】:

  • 您甚至需要能够创建此类的实例吗?为什么不让构造函数private 和所有方法static
  • 您可以在父类型中执行此操作(并使方法成为最终方法),但这一切都闻起来很糟糕。
  • 你有没有返回值且没有状态的方法?
  • 我用一个更复杂的例子编辑了这个问题,以澄清为什么静态不起作用
  • @Charlie - 谢谢,这两个选项对我来说都不是很好,选项一需要在其他模块的构造函数中传递大量模块。选项二是我在发现这种模式之前曾经拥有的,当你试图找出应该安装的所有缺失模块时,你最终会陷入一个巨大的运行-错误-修复-运行循环。我用等号找到的模式对我来说很适合这种重用,我想帮助减少样板

标签: java equals code-reuse boilerplate


【解决方案1】:

您的类都可以扩展一个实现equals 和hashCode 的通用基类。在这种情况下,您必须用this.getClass() 替换对MyClass 的引用。

另外,instanceof 不是您想要的,因为当 obj 属于扩展当前类的类时,它也会返回 true。这不仅不太可能是您想要的,而且还会违反 equals 的约定,因为当您将基类的实例与扩展类的实例进行比较时,调用 equals 方法会很重要(派生的是 instanceof base,但 base 不是 instanceof 派生的)。

您的基类中的实现如下所示:

@Override
public boolean equals(Object obj) {
   this.getClass().equals(obj.getClass())
}

@Override
public int hashCode() {
  return this.getClass().hashCode();
}

关于一个无关的说明:当对象没有内部状态时,为什么不使用Singleton pattern

【讨论】:

  • 使用单例模式是一种可能的选择,它有点违背我编写的其他类所使用的模式,并且可能会导致一些关于从哪里获取特定实例的混乱,即,有些类是通过 new 创建的,有些是通过单例模式创建的,但两者都在同一个地方使用。
  • 子类化可以工作,但我必须小心避免与多重继承相关的陷阱。我想在简单的层次结构中使用它对简单的情况很有用,你总是可以在更复杂的情况下自己编写它
  • 另外,instanceof 不是你想要的,因为当 obj 属于扩展当前类的类时它也会返回 true - 我认为它适用于这种情况,因为我的课是最后的
  • @Matt - "...我必须小心避免与多重继承相关的陷阱" ...Java 不允许多重继承。跨度>
  • "...Java 不允许多重继承" - 这就是我的意思,没有多重继承,所以我必须小心地将这个基类包含在我的正确位置类层次结构以实现最佳重用同时也保持最佳灵活性,IE不强迫这个规则让那些不想要或不需要它但确实想要与层次结构中的某个类相关联的其他功能的用户咽喉
猜你喜欢
  • 1970-01-01
  • 2021-11-13
  • 1970-01-01
  • 2016-12-13
  • 1970-01-01
  • 2021-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多