JetBrains' @Contract annotation

How does the org.jetbrains.annotations.Contract annotation work? How does IntelliJ IDEA support it?


Solution 1:

First off, I should say that this annotation is only for IDEA to use to check for possible errors. The Java compiler will ignore it almost entirely (it'll be in the compiled artifact but have no effect). Having said that...

The goal of the annotation is to describe a contract that the method will obey, which helps IDEA catch problems in methods that may call this method. The contract in question is a set of semi-colon separated clauses, each of which describes an input and an output that is guaranteed to happen. Cause and effect are separated by ->, and describe the case that when you provide X to the method, Y will always result. The input is described as a comma-separated list, describing the case of multiple inputs.

Possible inputs are _ (any value), null, !null (not-null), false and true, and possible outputs adds fail to this list.

So for example, null -> false means that, provided a null input, a false boolean is the result. null -> false; !null -> true expands on this to say that null will always return false and a non-null value will always return true, etc. Finally, null -> fail means the method will throw an exception if you pass it a null value.

For a multiple-argument example, null, !null -> fail means that, in a two-argument method, if the first argument is null and the second is not null, an exception will be thrown, guaranteed.

If the method does not change the state of the object, but just returns a new value, then you should set pure to true.

Solution 2:

The official documentation specifies the formal grammar of all supported and recognized values for the annotation.

In layman's terms:

  • A contract can have 1 or more clauses associated with it
  • A clause is always [args] -> [effect]
  • Args are 1 or more constraints, which are defined as any | null | !null | false | true
  • Effects are only one constraint or fail

Let's run through a quick example - one of my favorites is, "Whatever you pass into this method, it will throw an exception."

@Contract("_-> fail")
public void alwaysBreak(Object o) {
    throw new IllegalArgumentException();
}

Here, we're using _, or "any", to indicate that regardless of what we pass into this method, we're going to throw an exception.

What if we lied and said that this method was going to return true unconditionally?

@Contract("_-> true")
public void alwaysBreak(Object o) {
    throw new IllegalArgumentException();
}

IntelliJ raises some warnings about it.

enter image description here

It's also (obviously) upset that we said we were returning a boolean when we're in a void method...

enter image description here


The main times you'll find yourself wanting to use @Contract is when:

  • You want to guarantee that you return true or false
  • You want to guarantee that you return a non-null value given constraints
  • You want to make clear that you can return a null value given constraints
  • You want to make clear that you will throw an exception given constraints

That's not to say that @Contract is perfect; far from it. It can't do very deep analysis in certain contexts, but having this in your code base allows your tooling to do this sort of analysis for free.