Annotation du contrat de JetBrains
Comment fonctionne l'annotation org.jetbrains.annotations.Contract
?
Comment IntelliJ IDEA le supporte-t-il?
3 réponses
tout d'abord, je dois dire que cette annotation est seulement pour IDEA à utiliser pour vérifier les erreurs possibles. Le compilateur Java l'ignorera presque entièrement (il sera dans l'artefact compilé mais n'aura aucun effet). Après avoir dit cela...
le but de l'annotation est de décrire un contrat que la méthode obéira, ce qui aide idée attraper des problèmes dans les méthodes qui peuvent appeler cette méthode. Le contrat en question est un ensemble de des clauses séparées, chacune décrivant une entrée et une sortie garanties. Les causes et les effets 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 _
(toute valeur), null
, !null
(not-null), false
et true
, et sorties possibles ajoute fail
à cette liste.
ainsi, par exemple, null -> false
signifie que, pourvu qu'un null
entre, un false
booléen est le résultat. null -> false; !null -> true
se développe sur ce à dire que null
sera toujours retour false
et non null
valeur toujours return true, etc. Enfin, null -> fail
signifie que la méthode lève une exception si vous passez une valeur nulle.
pour un exemple d'argument multiple, 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 lancée, garantie.
si la méthode ne change pas l'état de l'objet, mais retourne juste une nouvelle valeur, alors vous devez définir pure
à true.
la documentation officielle spécifie la grammaire formelle de toutes les valeurs supportées et reconnues pour l'annotation.
en termes simples:
- Un contrat peut avoir 1 ou plusieurs clauses associées
- une clause est toujours
[args] -> [effect]
- les Arguments sont 1 ou plusieurs contraintes, qui sont définis comme
any | null | !null | false | true
- les effets ne sont qu'une contrainte ou
fail
passons en revue un exemple rapide - l'un de mes favoris est," quoi que vous passiez dans cette méthode, il va jeter une exception."
@Contract("_-> fail")
public void alwaysBreak(Object o) {
throw new IllegalArgumentException();
}
ici, nous utilisons _
, ou "n'importe", pour indiquer que peu importe ce que nous passons dans cette méthode, nous allons jeter une exception.
Que faire si nous avons menti et dit que cette méthode allait rendre true
inconditionnellement?
@Contract("_-> true")
public void alwaysBreak(Object o) {
throw new IllegalArgumentException();
}
IntelliJ soulève quelques avertissements à ce sujet.
c'est aussi (évidemment) fâché que nous ayons dit que nous retournions un booléen alors que nous sommes dans une méthode void ...
La principal du temps vous aurez besoin de @Contract
, c'est quand:
- vous voulez Vous assurer que vous retourner true ou false
- vous voulez garantir que vous retournerez une valeur non nulle étant donné les contraintes
- Vous voulez faire comprendre que vous pouvez revenir à une valeur nulle au vu des contraintes
- vous voulez préciser que vous allez jeter une exception compte tenu des contraintes
cela ne veut pas dire que @Contract
est parfait; loin de là. Il ne peut pas faire d'analyse très approfondie dans certains contextes, mais avoir cela dans votre base de code permet à votre outillage de faire ce genre d'analyse gratuitement.
Comment fonctionne l'annotation
org.jetbrains.annotations.Contract
?
bien que les réponses précédentes soient informatives, Je ne crois pas qu'elles abordent le mot" travail " dans votre question. C'est-à-dire qu'ils n'expliquent pas ce que IntelliJ fait dans les coulisses pour mettre en œuvre leurs annotations afin que vous puissiez construire votre propre facilement à partir de zéro.
si vous regardez le code source ci-dessous, vous pourriez penser qu'il semble un peu exagéré compliqué (ou du moins verbeux) pour quelque chose d'aussi simple que @NotNull
. Je suis d'accord avec vous, et c'est une des raisons pour lesquelles j'évite généralement les clauses @Contract
qui ne sont pas "simples et simples" (comme @NotNull
) et à la place JavaDoc Mes prix directement.
I en général décourager l'utilisation d'annotations de contrat complexes - malgré la haine que je pourrais recevoir pour sauter ce nouveau train à la Mode-et voici quelques raisons pourquoi:
- les Annotations peuvent être complexes - p.ex. avoir plusieurs annotations imbriquées dans leurs propres définitions et/ou une "grammaire" avec l'apparence de Turing completeness . Cela peut conduire à un faux sentiment de confiance au moment de la compilation en masquant le vrai coupable d'un bug derrière des couches d'obscurité/abstraction et en omettant de générer les Avertissements initialement prévus.
- similaire mais contrairement à mon point précédent, annotations cachent souvent la logique copieuse du développeur dans une poignée de mots clés, produisant difficile à comprendre le code pour les humains et/ou un comportement inhabituel qui peut être difficile à débugger.
- la configuration de L'application est souvent vu masquant comme annotations. Regardez le Spring framework .
- la syntaxe pour définir contrats est par-et-large (IMHO) assez laid et Makefile-ish. par exemple, jetez un coup d'oeil à certaines des annotations de JetBrains définitions et les fichiers de soutien dispersés dans son rapport . Vous avez remarqué les nombreux fichiers XML et les nombreuses références personnelles? Je ne dirais pas que c'est amusant d'écrire et de soutenir, surtout compte tenu de la nature en constante évolution des annotations dirigées par les échanges entre Android et la plus grande communauté Java.
Quelques questions à considérer:
- est-ce que ça va trop loin quand le code source d'une annotation approche la complexité de la source même qu'il annote?
- le segment de code ci-dessous est-il vraiment beaucoup mieux que de simplement vérifier null à l'exécution et les exceptions de journalisation avec stacktraces? Inclure quelque chose comme cela oblige vos utilisateurs à lire à travers, comprendre, et peut-être corriger un autre ensemble de dépendances qui définissent vos annotations.
emprunté à encore un autre long post sur la sémantique des contrats qui, selon moi, ne sert que mon point de vue:
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
{
}
quand et quels contrats devez-vous utiliser?
je suggère de s'en tenir à ceux dont les intentions sont claires de leur nom, et d'éviter ceux qui ont leur propre sémantique et définitions du langage.
Un exemple d'utilisation de -en dépit du segment précédent-est @NotNull
, mais reste limitée lorsque tous les paramètres de l'objet doit être null.
un exemple du genre à éviter sont ceux comme Android et IntelliJ @Contract(...)
. Bien que j'aime leurs IDEs, les détails de leurs annotations sont assez compliquées et finalement transformées en une source de plus de problèmes et d'incompatibilité de plate-forme pour moi de traquer vers le bas (certes causé par ma propre ignorance dans la rédaction de nouveaux contrats presque 100% du temps, mais pourquoi est-il si difficile de l'obtenir correctement?)
Résumé /Conclusion
Annotations sont une grande 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, en transformant la documentation en code sémantique qui mène à quelque chose de sérieux énigmes et situations embarrassantes. Pire encore, ils donnent parfois une fausse impression de sécurité lors de la compilation en ne détectant pas les problèmes manifestes dans leurs propres implémentations. Tenez-vous en au très simple et évitez tout ce qui ressemble à un langage qui n'est pas Java (ce qui est ce que vous aviez l'intention d'écrire en premier lieu).
Lire La Suite
cette brève liste est un mélange de plutôt critique (W / optimisme!) provenant à la fois de StackOverflow et du web j'ai l'impression d'illustrer certains de mes points.
dans aucun ordre particulier:
- Annotations Java sont une Grosse Erreur
- Annotations, Annotations, Partout
- Arguments contre les annotations
- les annotations sont-elles mauvaises?
- Une Annotation Cauchemar
- Mal Annotations
et après tout cela, je viens de réaliser que j'ai peut-être encore omis de répondre à votre question originale dans son intégralité:)