使用继承来重用toString / equals / hashCode

时间:2018-02-19 13:13:50

标签: java inheritance reflection

继承类重用toString / equals / hashCode(实际上是基于反射并使用实际的类字段)是不是很糟糕? 例如:

public abstract class SomeAbstractObject {

private List<Field> collectTransientFields() {
    //
    return Arrays.stream(getClass().getDeclaredFields())
            .filter((f) -> f.getAnnotation(Transient.class) != null)
            .collect(Collectors.toList());
}

@Override
public String toString() {
    return ToStringBuilder.reflectionToString(this, DEFAULT_STYLE);
}


@Override
public boolean equals(Object o) {
    //
    List<String> transients = collectTransientFields().stream()
            .map(Field::getName)
            .collect(Collectors.toList());
    return EqualsBuilder.reflectionEquals(this, o, transients);
}

@Override
public int hashCode() {
    List<String> transients = collectTransientFields().stream()
            .map(Field::getName)
            .collect(Collectors.toList());
    return HashCodeBuilder.reflectionHashCode(this, transients);
}

private List<Field> collectTransientFields() {
    //
    return Arrays.stream(getClass().getDeclaredFields())
            .filter((f) -> f.getAnnotation(Transient.class) != null)
            .collect(Collectors.toList());
}

class A extends SomeAbstractObject
class B extends SomeAbstractObject

我对hashCode和IDE生成的实现进行了基准测试。 IDE为双字段类快速生成10 000。

另一方面,甚至还有库(jakarta)提供这种反射功能。

所以我有两个问题:

  1. 是否可以继承类以重用toString / equals / hashCode功能
  2. 是否可以使用toString / equals / hashCode的基于反射的实现,而不是代码生成或人工编写的实现

2 个答案:

答案 0 :(得分:2)

作为equalshashCode的一般实现,您的代码在我看来并不是一个好主意,原因如下:

  • 通过反射检索字段是一项非常昂贵的操作。你自己已经发现我们正在谈论大约10,000的因素
  • getDeclaredFields仅返回&#34;当前&#34;的字段类和没有超类的字段。在这种情况下,equalshashCode的实际实施会产生错误的结果。
  • 在实现类时,您需要记住,不应该参与相等检查的类的所有成员都需要声明transient。对于非可序列化的类,这是非常罕见的,因此容易被遗忘。

我不确定为什么你想要这样的事情,你可以通过不写一个杰出的equalshashCode方法来实现一个新课程可能节省的五分钟很容易被殴打您需要跟踪可能由通用实现导致的错误的时间。无论如何,你必须为equalshashCode编写一个测试用例,即使它的实现是通用的,所以我在这里看不到真正的好处。

编辑,最后的想法:如果你有问题,你想通过使用反射来解决它,那就再做一次。反射是一个强大的工具,但可能导致许多新问题,你应该尽可能避免它。换句话说:对于非常特殊的指甲,反射是非常特殊的锤子。不要尝试将它用于其他类型。

答案 1 :(得分:2)

对于类的语义正确的equals()和hashCode()实现对于Java库的许多部分至关重要,值得关注细节。

对equals()和hashCode()使用反射将盲目地比较所有字段。虽然对于许多情况(例如,您的类只是数据集合),这是可以的,但也有例外。那些字段如&#34; id&#34;,&#34; lastChangedDate&#34;,&#34; status&#34;等等?在比较中不包括它们是非常合理的。

我更愿意为每个类决定要比较哪些字段,哪些字段没有,我建议不要继承一个对我的特定类不了解的默认行为。