Comment le org.jetbrains.annotations.Contract
travail d'annotation? Comment IntelliJ IDEA le prend-il en charge?
Tout d'abord, je dois dire que cette annotation est uniquement destinée à IDEA à utiliser pour vérifier les erreurs possibles. Le compilateur Java l'ignorera presque entièrement (il ' ll sera dans l'artefact compilé mais n'a aucun effet.) Cela dit ...
Le but de l'annotation est de décrire un contrat auquel la méthode obéira, ce qui aide IDEA à détecter les problèmes dans les méthodes qui peuvent appeler cette méthode. Le contrat en question est un ensemble de clauses séparées par des points-virgules, chacune décrivant une entrée et une sortie dont la garantie est garantie. La cause et l'effet sont séparés par ->
, et décrivez le cas où lorsque vous fournissez X à la méthode, Y donnera toujours résultat. L'entrée est décrite comme une liste séparée par des virgules, décrivant le cas de plusieurs entrées.
Les entrées possibles sont _
(n'importe quelle valeur), null
, !null
(non nul), false
et true
, et les sorties possibles ajoutent fail
à cette liste.
Ainsi, par exemple, null -> false
signifie que, à condition qu'une entrée null
, un booléen false
soit le résultat. null -> false; !null -> true
développe ceci pour dire que null
va toujours retourner false
et une valeur nonnull
sera toujours return true, etc. Enfin, null -> fail
signifie que la méthode lèvera une exception si vous lui passez une valeur nulle.
Pour un exemple à plusieurs arguments, null, !null -> fail
signifie que, dans une méthode à deux arguments, si le premier argument est nul et le second n'est pas nul, une exception sera levée, garantie.
Si la méthode ne change pas l'état de l'objet, mais renvoie simplement une nouvelle valeur, vous devez définir pure
sur true.
La documentation officielle spécifie la grammaire formelle de toutes les valeurs prises en charge et reconnues pour l'annotation.
En termes simples:
[args] -> [effect]
any | null | !null | false | true
fail
Passons en revue un exemple rapide - l'un de mes favoris est, "Quoi que vous passiez dans cette méthode, il lèvera une exception."
@Contract("_-> fail")
public void alwaysBreak(Object o) {
throw new IllegalArgumentException();
}
Ici, nous utilisons _
, ou "any", pour indiquer que, indépendamment de ce que nous transmettons à cette méthode, nous allons lever une exception.
Et si nous mentions et disions que cette méthode allait renvoyer true
sans condition?
@Contract("_-> true")
public void alwaysBreak(Object o) {
throw new IllegalArgumentException();
}
IntelliJ émet quelques avertissements à ce sujet.
C'est aussi (évidemment) bouleversé que nous avons dit que nous retournions un booléen lorsque nous sommes dans une méthode void. ..
La plupart du temps, vous vous retrouverez à vouloir utiliser @Contract
est quand:
Cela ne veut pas dire que @Contract
est parfait; loin de là. Il ne peut pas effectuer une analyse très approfondie dans certains contextes, mais l'avoir dans votre base de code permet à votre outillage de faire ce type d'analyse gratuitement.
Comment fonctionne l'annotation
org.jetbrains.annotations.Contract
?
Bien que les réponses précédentes soient informatives, je ne pense pas qu'elles abordent le mot opérationnel "travail" dans votre question. Autrement dit, ils n'expliquent pas ce que fait IntelliJ en arrière-plan pour implémenter leurs annotations afin que vous puissiez facilement créer le vôtre à partir de zéro.
Si vous regardez le code source ci-dessous, vous pourriez penser qu'il semble un peu trop compliqué (ou du moins verbeux) pour quelque chose d'aussi simple que @NotNull
. Je serais d'accord avec vous, et c'est une des raisons pour lesquelles j'évite généralement les clauses similaires à @Contract
Qui ne sont pas "simples et simples" (comme @NotNull
) Et à la place JavaDoc mes prérequis directement.
I en général décourage l'utilisation d'annotations de contrat complexes - malgré la haine que je pourrais avoir pour avoir sauté ce nouveau train branché - et voici quelques raisons pour lesquelles:
Quelques questions à considérer:
Emprunté à un autre long article sur la sémantique des contrats qui, je dirais, ne fait que renforcer mon point:
import Java.lang.annotation.Documented;
import Java.lang.annotation.ElementType;
import Java.lang.annotation.Retention;
import Java.lang.annotation.RetentionPolicy;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;
/**
* This annotation can be applied to a package, class or method to indicate that the class fields,
* method return types and parameters in that element are not null by default unless there is: <ul>
* <li>An explicit nullness annotation <li>The method overrides a method in a superclass (in which
* case the annotation of the corresponding parameter in the superclass applies) <li> there is a
* default parameter annotation applied to a more tightly nested element. </ul>
* <p/>
* @see https://stackoverflow.com/a/9256595/14731
*/
@Documented
@Nonnull
@TypeQualifierDefault(
{
ElementType.ANNOTATION_TYPE,
ElementType.CONSTRUCTOR,
ElementType.FIELD,
ElementType.LOCAL_VARIABLE,
ElementType.METHOD,
ElementType.PACKAGE,
ElementType.PARAMETER,
ElementType.TYPE
})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNullByDefault
{
}
Je suggère de m'en tenir au genre dont les intentions sont claires comme le cristal de leur nom, et d'éviter celles qui ont leur propre ensemble de définitions sémantiques et langagières.
n exemple de celui à utiliser - malgré le segment précédent - est @NotNull
, Mais gardez-le limité à quand tous les paramètres d'objet doivent être nuls.
n exemple du genre à éviter sont ceux comme Android et @Contract(...)
d'IntelliJ. Bien que j'aime leurs IDE, les détails de leurs annotations sont assez compliqués et se sont finalement transformés en une source de plus de problèmes et d'incompatibilité de plate-forme pour moi à retrouver (certes causée par ma propre ignorance dans la création de nouveaux contrats presque 100% du temps, mais pourquoi est-il si difficile de bien faire les choses?)
Les annotations sont une excellente idée clairement générée par les programmeurs qui cherchent à "codifier" leur documentation. Je pense qu'ils sont allés trop loin ces derniers temps, transformant la documentation en code sémantique porteur de graves énigmes et de situations délicates. Pire encore, ils confèrent parfois un faux sentiment de sécurité au moment de la compilation en ne détectant pas les problèmes manifestes dans leurs propres implémentations. Restez dans le très simple et évitez tout ce qui ressemble à une langue qui n'est pas Java (c'est ce que vous aviez l'intention d'écrire en premier lieu).
Cette brève liste est un mélange de sources principalement critiques (avec optimisme!) De StackOverflow et du Web qui, selon moi, illustrent certains de mes points.
Dans aucun ordre particulier:
Et après tout cela, je viens de réaliser que je n'ai peut-être toujours pas répondu à votre question initiale dans son intégralité :)