5

我是Java编程的导师。我的学生目前被迫(不是我,而是一个不清楚的任务)为一个抽象类实现clone()equals()hashCode() 。

为抽象类实现 clone()、equals() 或 hashCode() 有意义吗?你能举一个有意义的例子吗?

我可以想象,当您有抽象类 a 的一些子类 x、y、z 时,这是有道理的。这些子类可能仅在方法的实现上有所不同,因此您不需要将这三个方法实现三次。但我无法想象会出现这种情况的任何情况。

4

6 回答 6

4

我不会实施clone().

equals()但是实现,hashCode()toString()为所有子类提供默认行为是有意义的。如果孩子没有添加新的班级成员或根据需要补充,他们可以选择使用它。

于 2013-01-07T17:29:40.483 回答
1

误读了你的问题,并认为你也问过toString- 同样的推理仍然适用

例如,基本上迭代集合并以用户友好的方式打印它的实现AbstractCollectiontoString()

直接子类:

AbstractList, AbstractQueue, AbstractSet, ArrayDeque, ConcurrentLinkedDeque

所以所有集合、列表和队列都共享该toString方法。如果需要,每个子类都可以自由地重新实现它。

作为另一个示例,equals实现低一级(在 AbstracList/Set 等中)。

另一方面,clone是在最终实现中实现(例如 ArrayList),而不是在抽象类中。

底线:有时它是有意义的,但有时它没有,它可能不应该是适用于所有情况的义务。

于 2013-01-07T17:27:51.347 回答
1

1)clone()方法非常有用,当你需要实现某种对象的深度复制,同时你不能只使用对这个对象的引用,而是需要它们的新实例。所以,重写这个方法是有意义的。

2)equals()并且hashCode是两个重要的方法,当您需要特殊行为时必须重写HashSet/HashMap它们,其功能取决于这些方法的实现。因此,覆盖它们也是有道理的。

于 2013-01-07T17:42:31.397 回答
1

一个主要问题clone()是有两种实现方式,派生类中的正确实现取决于其父类的功能。

如果父类及其所有祖先调用super.clone()直到调用到达object.clone(),那么派生类要做的正确的事情是调用super.clone然后替换任何封装目标对象的可变状态而不是它们的身份的“添加”成员,用封装相同状态的新对象。如果派生类不包含任何封装可变对象状态的附加成员,则派生类不需要做任何事情,clone而可以简单地使用父类的实现。

实现类的另一种方法clone()是让它包含一个构造函数,该构造函数接受其自己类型的参数,并链接到父构造函数,该构造函数同样执行此操作,然后将所有适当的属性“新”复制到类型中传入实例到新实例。这种方法的一个优点是即使基类以clone这种方式实现它也可以工作。然而,这种方法有两个缺点:

  1. 每个派生类型都必须重新实现 clone(),无论添加的成员是否封装了可变状态
  2. 每个派生类型都必须使用这种方法实现 clone(),而不是调用 super.clone()。
请注意,如果一个类型clone()通过调用来实现,super.clone()但有一个祖先没有,那么生成的对象最终将是父类型而不是派生类型——不是clone()应该采取的方式。

于 2013-01-07T19:50:21.703 回答
0

方法 clone()、equals() 和 hasCode() 通常必须在实现该抽象类的类中实现,因为它们需要抽象类中不存在的字段。

然而,在某些情况下,equals()、hascode() 和可能的 clone() 在抽象类中实现是有意义的。(例如,equals() 在抽象容器类中有意义,例如 Collections 等)

对于 hashCode() 和 clone() 我更怀疑这是否有意义。

于 2013-01-07T17:31:51.887 回答
0

能说得通吗?

  • 如果抽象类想要修改标准行为,特别是对于子类无法访问的状态,覆盖clone()是有意义的。clone()
  • 如果抽象超类正在执行关于平等行为的合同,则覆盖equals()hashCode()有意义。该契约可以由它自己指定,也可以由它实现的超类或接口指定。

它是否合适,即我们是否想要设计一个抽象超类来覆盖这些?

  • 正如答案中其他地方所提到的,该clone()机制本身很少适用。一种适当的实现可能是显式地抛出 aCloneNotSupportedException并生成它final,并以这种方式防止子类实现Cloneable
  • 对于equals()hashCode()明智的情况也是适当的情况。如果一个抽象超类本身可以满足平等契约规范,它应该这样做。并且它应该制定equals()hashCode()方法final,以防止LSP违规。我会引用 Josh Bloch 的话:

没有办法在保留 equals 契约的同时扩展一个可实例化的类并添加一个值组件——Effective Java 2nd ed。

于 2013-01-07T19:09:24.230 回答