views:

168

answers:

4

Can we have a "not override a concrete method ..." compile time error when implementing interfaces ?

An example to be more clear :

I build up a framework containing interfaces. To use the framework developers need to implements some interfaces. But if they don't override equals(Object object) and hashCode() Object methods the internal API logic will be broken !!

All that is mentioned in the javadoc but I want to have a compile time error or maybe a runtime exception when an interface is implemented without overriding some concretes methods.

+1  A: 

I'm afraid SO is the wrong place to ask. We answer questions about languages, we don't make the languages.

Sun has a Community Process in place where you can make suggestions for improvement or outright bug reports. I can dig you up links if you like.

I suspect it won't get done, though. If it wasn't considered a good idea until now, it probably won't be.

Something to help you in the short run might be standalone style checking programs. CheckStyle comes to mind, it's probably the biggest name out there, it's free-as-in-beer and offers a WEALTH of options. I suspect, but I'm not sure, that "not implementing hashCode() and equals() is somewhere in there too.

Carl Smotricz
+1 : thx for the CheckStyle link I never heard about it ... but in this case I think your idea of a self made hashCode() and equals() method will be more appropriate ...
wj
Luckily Java/Sun provided a mechanism to enforce that!
notnoop
Carl, I don't think the OP wants a language change. Instead they are hoping to find a solution within the language as it exists.
Peter Recore
@Peter: Yep, looks that way. It seems notnoop demonstrated a great solution.
Carl Smotricz
+1  A: 

I just thought of a possible alternative:

Your interface (or possibly abstract methods) could specify methods (e.g.) equals2() and hashCode2(). These would be mandatory to implement to any implementor of the interface, of course. You could thus force your library consumers to code something.

And your library classes, that work with implementors of your interface, could then use these "custom" functions as replacements or implementations of the "real" functions, and to the re-definition themselves.

It's not a very elegant concept, and not very fleshed out, but you may be able to make something of it.

Carl Smotricz
+1 : thx !! it is a very good lead ...
wj
+2  A: 

You can't do that with interfaces, but you can do that with abstract classes. E.g.

public interface Entity {
    //...
}

public abstract class AbstractEntity implements Entity {

    @Override
    public abstract boolean equals(Object other);

    @Override
    public abstract int hashCode();

}

and tell developers to extend AbstractEntity whenever they want to implement an Entity.

If you really want the rule to be enforced you'd need some sort of runtime check, like a bean post-processor in Spring.

Mirko Nasato
+1: It was my first thought ... but I wanted to keep a free option for developers to inherit from others classes :) ... but you're right it's probably the easiest way ...
wj
+5  A: 

Java ships with annotation processor capabilities, starting from Java 6: Source Code Analysis. (Technically it's part of Java 5, but Java 6 integrated it into the compiler phase rather than a special tool). Sun provides this Getting Started guide.

An annotation processor gets invoked by the compiler while building the project, and may issue errors just like the compiler does. It's a neat way to enforce more rules than Java specifies.

Here is a sample Processor that would perform the check you desired:

@SupportedSourceVersion(SourceVersion.RELEASE_6)
@SupportedAnnotationTypes("*")
public class CheckMethodOverride extends AbstractProcessor {
    // returns true if the class has a method with the specified method name
    // and specified number of parameters
    private static boolean hasMethod(TypeElement clazz, String methodName, int arity) {
        for (ExecutableElement method : 
                 ElementFilter.methodsIn(clazz.getEnclosedElements())) {
            if (method.getSimpleName().equals(methodName)
                    && method.getParameters().size() == arity)
                return true;
        }
        return false;
    }

    // the interface whose subclasses must override hashCode and equals
    TypeMirror interfaceToCheck;

    @Override
    public void init(ProcessingEnvironment env) {
        interfaceToCheck = env.getElementUtils().getTypeElement("com.notnoop.myinterface").asType();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations,
            RoundEnvironment roundEnvironment) {
        for (TypeElement e :
            ElementFilter.typesIn(roundEnvironment.getRootElements())) {
            if (this.processingEnv.getTypeUtils()
                     .isSubtype(e.asType(), interfaceToCheck)
                && (!hasMethod(e, "equals", 0)
                    || !hasMethod(e, "hashCode", 0))) {
                processingEnv.getMessager().printMessage(Kind.ERROR,
                    "Class " + e + " doesn't override hashCode or equals", e);
            }
        }
        return true;
    }
}

The annotation processors can be specified directly in the compiler options (by adding -processor com.notnoop.CheckMethodOverride to the compiler options), or adding a META-INF/services file to your classpath which contains the name of the processor (com.notnoop.CheckMethodOverride). The forlder META-INF can live in the root source directory of the project (or resource directory if using maven).

This works with any command line compiler. I have no idea how to activate it in the IDEs though.

notnoop
I don't fully understand this, but it looks amazing, and probably an elegant solution to the OP's problem. +1.
Carl Smotricz
+1: elegant solution !!! i will try it...
wj
This is a nice example of using "Class Literals as Runtime-Type Tokens" in annotations, http://java.sun.com/docs/books/tutorial/extra/generics/literals.html. It's reminiscent of Neal Gafter's "Super Type Tokens", http://gafter.blogspot.com/2006/12/super-type-tokens.html
trashgod