Search code examples
javainheritanceinterfaceabstract-classhamcrest

Why insist all implementations of an interface extend a base class?


I was just looking at the Java Hamcrest code on GitHub, and noticed they employed a strategy that seemed unintuitive and awkward, but it got me wondering if I'm missing something.

I noticed in the HamCrest API that there is an interface Matcher and an abstract class BaseMatcher. The Matcher interface declares this method, with this javadoc:

    /**
     * This method simply acts a friendly reminder not to implement Matcher directly and
     * instead extend BaseMatcher. It's easy to ignore JavaDoc, but a bit harder to ignore
     * compile errors .
     *
     * @see Matcher for reasons why.
     * @see BaseMatcher
     * @deprecated to make
     */
    @Deprecated
    void _dont_implement_Matcher___instead_extend_BaseMatcher_();

Then in BaseMatcher, this method is implemented as follows:

    /**
     * @see Matcher#_dont_implement_Matcher___instead_extend_BaseMatcher_()
     */
    @Override
    @Deprecated
    public final void _dont_implement_Matcher___instead_extend_BaseMatcher_() {
        // See Matcher interface for an explanation of this method.
    }

Admittedly, this is both effective and cute (and incredibly awkward). But if the intention is for every class that implements Matcher to also extend BaseMatcher, why use an interface at all? Why not just make Matcher an abstract class in the first place and have all other matchers extend it? Is there some advantage to doing it the way Hamcrest has done it? Or is this a great example of bad practice?

EDIT

Some good answers, but in search of more detail I'm offering a bounty. I think that the issue of backwards / binary compatibility is the best answer. However, I'd like to see the issue of compatibility elaborated on more, ideally with some code examples (preferably in Java). Also, is there a nuance between "backwards" compatibility and "binary" compatibility?

FURTHER EDIT

January 7, 2014 -- pigroxalot provided an answer below, linking to this comment on Reddit by the authors of HamCrest. I encourage everyone to read it, and if you find it informative, upvote pigroxalot's answer.

EVEN FURTHER EDIT

December 12, 2017 -- pigroxalot's answer was removed somehow, not sure how that happened. It's too bad... that simple link was very informative.


Solution

  • The git log has this entry, from December 2006 (about 9 months after the initial checkin):

    Added abstract BaseMatcher class that all Matchers should extend. This allows for future API compatability [sic] as the Matcher interface evolves.

    I haven't tried to figure out the details. But maintaining compatibility and continuity as a system evolves is a difficult problem. It does mean that sometimes you end up with a design that you would never, ever, ever have created if you had designed the whole thing from scratch.