Вопрос

Preamble: Let's say I aim to cover all my classes with test coverage.

Despite the common opinion (expecially in Java community) to "not pay too much attention to Cyclomatic Complexity (CC) and code coverage", I really think that these information are extremely useful: When you have a very high CC, or an uncovered zone in your code, very likely you wrote a dead code, or either is a smell that you have problems in design. In my personal experience I can say that many time dead code is code smell.

This problem become a little tricky when you have to deal with auto-generated code, and that's the point, how can I easily deal with hashcode and equals autogenerated methods? I came across this question in StackOverflow and I realized some very good point:

https://stackoverflow.com/questions/5764380/unit-testing-equals-and-hashcode-a-complexity-story

But this question is not a duplicate because I want to use a different approach:

The best options available in the answers of that question are, as far as I can see, EqualsBuilder of Apache Commons Lang or Guava. Both uses reflection (correct me if I'm wrong) and that could be a problem for performance in some situations.

But my actual question is: Since most of the times both equals and hashcode methods are not covered, is that a hint that this particular class does not need those 2 methods? Since many Java developers a lot of time generate equals and hashcode even when not needed, what am I missing? Shouldn't be a better solution to generate those methods only when you need, for instance, to sort objects of that class, to use that objects as an Hashtable key, and so on?

As I said, I think that goals of low complexity and high coverage are admirable, but the solution of using EqualsTester , seems to me like "Force to cover code that you don't actually need in your program".

Это было полезно?

Решение

The story of equals() combines several unfortunate design decisions that are now impossible to change, and that every Java user just has to live with one way or another: it is deeply woven into the semantics of the standard container types, it is automatically inherited by every user-defined class but with a behaviour that is usually not what you want, and defining it without also defining hashCode() silently results in undefined behaviour (i.e. the language imposes certain requirements on you but has no way of warning you if they are unsatisfied.)

You can get along by simply never defining the method IF you never use such objects anywhere that would expose the unwanted behaviour. But since software development is a cooperative game, this requires an iron-clad, no-exceptions, disciplined rule of "Absolutely no Foo widgets in Sets, ever!!" that everyone involved with the code must know and follow 100%.

It can be worthwhile to impose such a rule in order to gain some performance, just as with other very fundamental principles of coding - e.g. "No null return values, ever!" is a common strategy to save a lot of checking for null. But this depends on how much you really profit from the small simplification for each class. The downside is that you have added another unspoken assumption that all users of your class must know and follow, or silently suffer from undefined behaviour.

Лицензировано под: CC-BY-SA с атрибуция
scroll top