I've recently added the RI for JSR305 into my project and have been adding annotations to my interfaces like this:
public interface AccountService
{
@Nullable AccountResult consolidate( @Nonnull Date from, @Nonnull Date to )
}
In the spirit of the JSR (as described here), do you think I'm misusing the annotations by using them in interfaces? I want to indicate that the return value can be null but I think the @Nullable applies more to the method "consolidate". I don't add them to the implementations because when I'm coding, the code for the interface is my guiding light.
You can see @NonNull String
as a strict subclass of String. After all, any non-null String is
definitely an 'instanceof' @Nullable String
, but any instanceof @Nullable String
may not be an
instance of @NonNull String
(it wouldn't be if it was null, for example).
Looking at it that way, @Nullable
and @NonNull
are type information, and are therefore perfectly
reasonable in interfaces. You are indicating to implementers that they may return null, and that
they do not have to worry about input nulls, and you are indicating to callers that they must not
pass in null, and that they should expect nulls out.
Of course, while this is all very reasonable, vanilla javac v1.6 certainly doesn't enforce any of these rules the way it enforces type safety for actual types. But one can dream, or one could use something like pmd or findbugs to take the job of verifying these annotations.
The @NonNull
and @Nullable
annotations aren't enough for a complete nullity typing system, though. I
don't really know why JSR305 doesn't address this, but there's a third type: @MaybeNull
. It would
show up inside of generics parameters; anywhere else it would have the same meaning as @Nullable
.
public static <T> void addIfNotNull(List<@MaybeNull T> list, @Nullable T item) {
if ( item != null ) list.add(item);
}
If the annotation on the first T
there is @Nullable
, then you could not pass non-null lists in,
which would make for a rather useless API. On the other hand, if it was @NonNull, you could not pass
a nullable list in. In practice, it doesn't matter what you move in there, it'll
NullPointerException
, andSo, you need a way to express: I don't
care if this particular T
is Nullable or not; I will null-check when I read from it, and I will
never write nulls, so it doesn't matter.
The difference between @MaybeNull
and @Nullable
is analogous to the difference between ? extends Number
and Number
in generics. Outside of generics they mean the same thing, but in generics
there's a difference.
So, don't keep your hopes up for a rigid typecheck anytime soon.
@Inherited
only works for classes, but one would imagine something similar for annotated return
types and annotated parameters.
I think it depends on why you are adding the annotations.
If you're just adding them for reference so that implementers of the interface know your intended design, then that's fine.
I use annotations for transactional processing and security role enforcement, so my annotations are at the implementation level. What's nice about this is that AOP processor in my Spring container can test for the presence of this or that annotation and apply appropriate proxy classes to do things like define a transactional boundary or ensure that the user is authorized for some particular method call.
So, if you're planning to key any processing off of the presence or absence of the annotation, make sure your implementation is annotated. If its just for reference, its OK to annotate just the interface.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With