Search code examples
javaannotations

How to allow only classes annotated with some annotation in method parameters in Java?


Let's suppose I have annotation @MyAnnotation and two classes:

@MyAnnotation
class Foo {
}

class Bar {
}

and some method that needs class as parameter

someMethod(Class<?> klass)

Is it possible to restrict someMethod parameter only to classes that are annotated with @MyAnnotation? I mean:

someMethod(Foo.class) //Must work
someMethod(Bar.class) //Compiler error

If yes, how to do that?


Solution

  • Yes, this is possible.

    As a general rule, if a property is expressed using an annotation, you will need to use an annotation processor.

    Here is a full example that uses the Checker Framework.

    File SO61029446.java:

    public class SO61029446 {
      void someMethod(Class<? extends @MyAnnotation Object> klass) {}
    
      void client() {
        someMethod(Foo.class); // Must work
        someMethod(Bar.class); // Compiler error
      }
    }
    
    @MyAnnotation
    @SuppressWarnings("subtyping")
    class Foo {}
    
    class Bar {}
    

    File MyAnnotation.java:

    @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
    @SubtypeOf(UnknownMyAnnotation.class)
    @interface MyAnnotation {}
    
    @DefaultQualifierInHierarchy
    @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
    @SubtypeOf({})
    @interface UnknownMyAnnotation {}
    

    Now, run these commands (after installing the Checker Framework):

    javacheck MyAnnotation.java
    javacheck -g SO61029446.java -processor org.checkerframework.common.subtyping.SubtypingChecker -Aquals=MyAnnotation,UnknownMyAnnotation SO61029446.java
    

    The output of the second command is:

    SO61029446.java:11: error: [argument.type.incompatible] incompatible types in argument.
        someMethod(Bar.class); // Compiler error
                      ^
      found   : @UnknownMyAnnotation Class<@UnknownMyAnnotation Bar>
      required: @UnknownMyAnnotation Class<? extends @MyAnnotation Object>
    

    The compiler has complained about the illegal invocation but has permitted the legal invocation, just as you requested.

    A few notes:

    • The javacheck command is as described in the Checker Framework Manual.
    • There are two javacheck commands because the first one makes the annotations available on the classpath for the second one.
    • The @SuppressWarnings("subtyping") annotation on the Foo class might not be needed, depending on your actual code. (This is a toy example.)
    • Slaw's answer starts with first sentence "No, this is not possible.", which is incorrect. Slaw's different approach to solving the problem without annotations is not a bad one (in some ways it's better because it uses just the Java compiler without any additional tool), but it does not answer this question about using annotations.