Java 为抽象类实现clone()、equals()或hashCode()有意义吗?

Java 为抽象类实现clone()、equals()或hashCode()有意义吗?,java,clone,abstract-class,equals,hashcode,Java,Clone,Abstract Class,Equals,Hashcode,我是Java编程的导师。我的学生们目前被迫(不是我,而是一个不明确的任务)去实现一个抽象类 为抽象类实现clone()、equals()或hashCode()有意义吗?你能举一个这样做有意义的例子吗 我可以想象,当你有抽象类a的一些子类x,y,z时,这是有意义的。这些子类可能只在方法的实现上有所不同,因此不需要三次实现这三个方法。但我无法想象任何情况下会出现这种情况。误读了你的问题,认为你也问了关于toString-同样的道理也适用 例如,AbstractCollection实现了toStrin

我是Java编程的导师。我的学生们目前被迫(不是我,而是一个不明确的任务)去实现一个抽象类

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

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

误读了你的问题,认为你也问了关于
toString
-同样的道理也适用

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

直接子类:

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

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

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


一句话:有时它是有意义的,但有时它不是,它可能不应该是适用于所有情况的义务。

我不会实现
clone()

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

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

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

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

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

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

这有意义吗

  • 如果抽象类希望重写
    clone()
    ,那么重写就有意义了 修改
    clone()
    的标准行为,尤其是关于 子类无法访问的状态
  • 如果抽象超类在平等行为上强制执行契约,那么重写
    equals()
    hashCode()
    是有意义的。该契约可以由其自身指定,也可以由其实现的超类或接口指定
它是否合适,也就是说,我们是否希望设计一个抽象超类来覆盖这些超类

  • 正如答案中其他地方提到的那样,
    clone()
    机制本身很少适用。一个合适的实现可能是明确地抛出一个
    CloneNotSupportedException
    ,并使其成为
    final
    ,并以这种方式防止子类实现
    Cloneable
  • 对于
    equals()
    hashCode()
    来说,合理的情况也是合适的情况。如果抽象超类本身能够满足平等契约规范,那么它应该这样做。它应该使
    等于()
    hashCode()
    方法
    final
    ,以防止LSP违规。我要引用Josh Bloch的话:
没有办法延长期限 实例化类,并在保留相等值的同时添加值组件 合同-第二版生效


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

如果父类及其所有祖先调用
super.clone()
,直到调用到达
object.clone()
,那么派生类应该做的事情就是调用
super.clone
,然后替换任何“添加”的成员,这些成员封装了目标对象的可变状态,但不是它们的标识,使用封装相同状态的新对象。如果派生类不包含任何封装可变对象状态的添加成员,则派生类不需要使用
clone
执行任何操作,只需使用父类的实现即可

类实现
clone()
的另一种方法是让它包含一个构造函数,该构造函数接受它自己类型的参数,并链接到一个父构造函数,父构造函数也接受它自己类型的参数,然后将所有适当的“新”属性从传入实例复制到新实例。这种方法的一个优点是,即使基类以这种方式实现
clone
,它也可以工作。但是,这种方法有两个缺点:
  • 每个派生类型都必须重新实现clone(),而不管是否有任何添加的成员封装可变状态
  • 每个派生类型都必须使用这种方法实现clone(),而不是调用super.clone()。 请注意,如果一个类型通过调用
    super.clone()
    实现了
    clone()
    ,但是有一个祖先没有实现,那么结果