简体   繁体   English

使用继承重用toString / equals / hashCode

[英]Use inheritance to reuse toString/equals/hashCode

Does it bad practice inherit class to reuse toString/equals/hashCode (that actually based on reflection and use actual class field for it)? 不好的做法是继承类以重用toString / equals / hashCode(实际上基于反射并为其使用实际的类字段)吗? For example: 例如:

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

I benchmarked this implementation of hashCode and IDE-generated one. 我对hashCode和IDE生成的该实现的基准进行了基准测试。 IDE-generated 10 000 faster for two-field class. IDE为两场课程生成的速度快了10000。

On the other hand there is even library (jakarta) that provide this reflection functionality. 另一方面,甚至有提供此反射功能的库(雅加达)。

So I have two question: 所以我有两个问题:

  1. Is it ok to inherit classes for reuse toString/equals/hashCode functionality 可以继承类以重用toString / equals / hashCode功能吗
  2. Is it ok to use reflection-based implementation of toString/equals/hashCode instead of code-generated or human-written one 是否可以使用基于反射的toString / equals / hashCode实现而不是代码生成或人工编写的实现

Your code as a general implementation for equals and hashCode is in my eyes not a good idea for a couple of reasons: 在我看来,将代码作为equalshashCode的通用实现不是一个好主意,原因有以下几个:

  • Retrieving the fields by reflection is a very costly operations. 通过反射检索场是非常昂贵的操作。 You yourself already found out that we're talking about a factor of about 10,000 您自己已经发现我们正在谈论的因子约为10,000
  • getDeclaredFields only returns the fields of the "current" class and no fields of a superclass. getDeclaredFields仅返回“当前”类的字段,而不返回超类的字段。 Your actual implementation of equals and hashCode would produce wrong results in such a case. 在这种情况下, equalshashCode实际实现会产生错误的结果。
  • When implementing a class you need to keep in mind that all members of a class that aren't supposed to be involved in an equality-check need to be declared transient . 在实现一个类时,需要记住,该类中所有不应该参与相等检查的成员都必须声明为transient For non-serializable classes this is quite uncommon and is therefor easily forgotten. 对于不可序列化的类,这很不常见,因此很容易被遗忘。

I'm not sure why you want such a thing anyway, the five minutes you might save while implementing a new class by not writing a distinguished equals and hashCode method are easily beaten by the time you need to track down a bug that might result from the generic implementation. 我不确定为什么还是要这样,在不hashCode可能由以下原因导致的错误时,不编写可区分的equalshashCode方法在实现新类时可能节省的五分钟时间通用实现。 You have to write a testcase for equals and hashCode anyway, even if its implementation is generic, so I don't see a real benefit here. 无论如何,您都必须为equalshashCode编写一个测试用例,即使其实现是通用的,因此在这里我看不到真正的好处。

Edit, final thought: If you have a problem and you think of solving it by using reflection, thing again. 编辑,最后的想法:如果您有问题,并且想通过反射来解决,那就再做一次。 Reflection is a powerful tool but can lead to so many new problems that you should avoid it wherever possible. 反射是一个功能强大的工具,但会导致许多新问题,因此应尽可能避免使用它。 In other words: Reflection is a very specific hammer for very specific nails. 换句话说:反射是针对非常特殊的指甲的非常特殊的锤子。 Don't try to use it for other types. 不要尝试将其用于其他类型。

Having the semantically correct equals() and hashCode() implementations for a class is crucial for many parts of the Java library, and it deserves attention to details. 对于Java类的许多部分,在类上具有语义正确的equals()和hashCode()实现对于Java库的许多部分都是至关重要的,并且应注意细节。

Using reflection for equals() and hashCode() will blindly compare all fields. 对equals()和hashCode()使用反射将盲目地比较所有字段。 Although that will be OK for many cases (where your class is just a collection of data), there are exceptions. 尽管在许多情况下(您的类只是数据的集合)可以这样做,但也有例外。 What about fields like "id", "lastChangedDate", "status" and so on? 那么“ id”,“ lastChangedDate”,“ status”等字段呢? It's quite plausible to not include them in the comparison. 在比较中不包括它们是很合理的。

I prefer to decide for every single class which fields to compare and which not, and I'd recommend against inheriting a default behaviour that can't know anything about my specific class. 我更愿意为每个类决定要比较的字段,而不是要比较的字段,并且建议不要继承对我的特定类一无所知的默认行为。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM