@Contract-Anmerkung von JetBrains

Lesezeit: 8 Minuten

Benutzer-Avatar
SpongeBob

Wie funktioniert die org.jetbrains.annotations.Contract Anmerkungsarbeit? Wie unterstützt IntelliJ IDEA dies?

Benutzer-Avatar
dcsohl

Zunächst einmal sollte ich sagen, dass diese Anmerkung nur von IDEA verwendet werden kann, um nach möglichen Fehlern zu suchen. Der Java-Compiler ignoriert es fast vollständig (es befindet sich im kompilierten Artefakt, hat aber keine Auswirkung). Nachdem ich das gesagt habe …

Das Ziel der Annotation ist es, a zu beschreiben Vertrag dass die Methode gehorcht, was IDEA hilft, Probleme in Methoden zu erkennen, die diese Methode aufrufen können. Der fragliche Vertrag besteht aus einer Reihe von durch Semikolons getrennten Klauseln, von denen jede eine Eingabe und eine Ausgabe beschreibt, die garantiert ausgeführt werden. Ursache und Wirkung werden durch getrennt ->und beschreiben Sie den Fall, dass Y dies tut, wenn Sie X für die Methode bereitstellen stets Ergebnis. Die Eingabe wird als kommagetrennte Liste beschrieben, die den Fall mehrerer Eingaben beschreibt.

Mögliche Eingaben sind _ (beliebiger Wert), null, !null (nicht null), false und trueund mögliche Ausgänge fügt hinzu fail zu dieser Liste.

Also zum Beispiel null -> false bedeutet, dass, sofern a null Eingabe, a false boolean ist das Ergebnis. null -> false; !null -> true erweitert dies, um das zu sagen null Wille stets Rückkehr false und ein Nicht-null Wert wird stets true zurückgeben usw. Schließlich null -> fail bedeutet, dass die Methode eine Ausnahme auslöst, wenn Sie ihr einen Nullwert übergeben.

Für ein Beispiel mit mehreren Argumenten: null, !null -> fail bedeutet, dass bei einer Methode mit zwei Argumenten garantiert eine Ausnahme ausgelöst wird, wenn das erste Argument null und das zweite nicht null ist.

Wenn die Methode den Zustand des Objekts nicht ändert, sondern nur einen neuen Wert zurückgibt, sollten Sie festlegen pure zu wahr.

Benutzer-Avatar
Makoto

Das amtliche Dokumentation spezifiziert die formale Grammatik aller unterstützten und anerkannten Werte für die Anmerkung.

Laienhaft ausgedrückt:

  • Einem Vertrag können 1 oder mehrere Klauseln zugeordnet sein
  • Eine Klausel ist stets [args] -> [effect]
  • Args sind 1 oder mehrere Einschränkungen, die definiert sind als any | null | !null | false | true
  • Effekte sind nur eine Einschränkung bzw fail

Lassen Sie uns ein kurzes Beispiel durchgehen – einer meiner Favoriten ist: „Was auch immer Sie an diese Methode übergeben, es wird eine Ausnahme auslösen.“

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

Hier verwenden wir _oder “beliebig”, um anzuzeigen, dass unabhängig davon, was wir an diese Methode übergeben, wir eine Ausnahme auslösen werden.

Was wäre, wenn wir lügen und sagen würden, dass diese Methode zurückkehren würde? true bedingungslos?

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

IntelliJ warnt davor.

Geben Sie hier die Bildbeschreibung ein

Es ist auch (offensichtlich) verärgert, dass wir gesagt haben, dass wir einen booleschen Wert zurückgeben, wenn wir in a sind Leere Methode…

Geben Sie hier die Bildbeschreibung ein


Die wichtigsten Zeiten, die Sie selbst verwenden möchten @Contract ist, wenn:

  • Sie möchten sicherstellen, dass Sie wahr oder falsch zurückgeben
  • Sie möchten sicherstellen, dass Sie bei gegebenen Einschränkungen einen Nicht-Null-Wert zurückgeben
  • Sie möchten klarstellen, dass Sie bei bestimmten Einschränkungen einen Nullwert zurückgeben können
  • Sie möchten klarstellen, dass Sie bei bestimmten Einschränkungen eine Ausnahme auslösen

Das soll nicht heißen @Contract ist perfekt; weit davon entfernt. Es kann in bestimmten Kontexten keine sehr tiefen Analysen durchführen, aber wenn Sie dies in Ihrer Codebasis haben, können Ihre Tools diese Art von Analyse kostenlos durchführen.

Benutzer-Avatar
Verschränkte Schleifen

Wie funktioniert die org.jetbrains.annotations.Contract Anmerkungsarbeit?

Obwohl die vorherigen Antworten informativ sind, habe ich nicht das Gefühl, dass sie das operative Wort “Arbeit” in Ihrer Frage ansprechen. Das heißt, sie erklären nicht, was IntelliJ hinter den Kulissen tut, um ihre Anmerkungen zu implementieren, damit Sie Ihre eigenen leicht von Grund auf neu erstellen können.

Wenn Sie sich den Quellcode unten ansehen, denken Sie vielleicht, dass er etwas zu kompliziert (oder zumindest ausführlich) für etwas so einfach Klingendes wie erscheint @NotNull. Ich würde Ihnen zustimmen, und das ist ein Grund, den ich im Allgemeinen vermeide @Contract-ähnliche Klauseln, die nicht “schlicht und einfach” sind (wie @NotNull) und stattdessen JavaDoc my prereqs direkt.

ich allgemein raten Sie davon ab, komplexe Vertragsanmerkungen zu verwenden– trotz des Hasses, den ich dafür bekommen könnte, diesen trendigen neuen Zug zu überspringen – und hier einige Gründe warum:

  • Anmerkungen können komplex sein– zB mehrere verschachtelte Anmerkungen in ihren eigenen Definitionen und/oder eine „Grammatik“ mit dem Aussehen von Vollständigkeit. Dies kann zur Kompilierzeit zu einem falschen Vertrauensgefühl führen, indem der wahre Schuldige eines Fehlers hinter Schichten von Unklarheit/Abstraktion maskiert wird und die ursprünglich beabsichtigten Warnungen nicht generiert werden.
  • Ähnlich, aber anders als mein vorheriger Punkt, Anmerkungen verbergen oft umfangreiche Logik vor dem Entwickler in einer Handvoll Schlüsselwörter, was zu schwer verständlichem Code für Menschen und/oder ungewöhnlichem Verhalten führt, das schwierig zu debuggen sein kann.
  • App-Konfiguration ist oft maskiert gesehen als Anmerkungen. Schauen Sie sich die an Frühlingsrahmen.
  • Die Syntax zum Definieren von Verträgen ist im Großen und Ganzen (IMHO) ziemlich hässlich und Makefile-artig. Werfen Sie beispielsweise einen Blick auf einige der JetBrains-Annotationsdefinitionen und unterstützenden Dateien über sein Repo verstreut. Beachten Sie die zahlreichen XML-Dateien und die zahlreichen Selbstreferenzen? Ich würde das kaum als Spaß beim Schreiben und Unterstützen bezeichnen, besonders wenn man bedenkt, dass sich Anmerkungen ständig weiterentwickeln, angeführt vom Hin und Her zwischen Android und der größeren Java-Community.

Einige Fragen zum Nachdenken:

  • Geht es zu weit, wenn sich der Quellcode einer Anmerkung der Komplexität der Quelle nähert, die er kommentiert?
  • Ist das folgende Codesegment wirklich so viel besser, als nur zur Laufzeit nach null zu suchen und Ausnahmen mit Stacktraces protokollieren? Das Einfügen von so etwas zwingt Ihre Benutzer dazu, einen anderen Satz von Abhängigkeiten, die Ihre Anmerkungen definieren, durchzulesen, zu verstehen und möglicherweise Fehler zu beheben.

Aus einem weiteren langen Beitrag über Vertragssemantik entlehnt, von dem ich argumentieren würde, dass er meinem Standpunkt nur weiter dient:

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
{
}

Wann und welche Verträge sollten Sie verwenden?

Ich schlage vor, sich an die Art zu halten, deren Absichten glasklar aus ihrem Namen hervorgehen, und solche mit ihrer eigenen Semantik und sprachähnlichen Definitionen zu vermeiden.

Ein Beispiel für eine zu verwendende– trotz des vorherigen Segments – ist @NotNullaber beschränken Sie es darauf, wann alle Objektparameter null sein müssen.

Ein Beispiel von der Sorte, die man vermeiden sollte sind solche wie Android und IntelliJ @Contract(...). Obwohl ich ihre IDEs liebe, sind die Details ihrer Anmerkungen ziemlich kompliziert und haben sich letztendlich in eine Quelle von mehr Problemen und Plattforminkompatibilität verwandelt, die ich aufspüren muss (zugegebenermaßen verursacht durch meine eigene Unwissenheit beim Verfassen neuer Verträge in fast 100 % der Fälle, aber warum ist es so schwierig, es richtig zu machen?)

Zusammenfassung / Fazit

Anmerkungen sind eine großartige Idee, die eindeutig von Programmierern entwickelt wurde, die ihre Dokumentation “kodifizieren” möchten. Ich habe das Gefühl, dass sie in letzter Zeit zu weit gegangen sind, indem sie die Dokumentation in semantiktragenden Code verwandelt haben, der zu einigen ernsthaften Rätseln und unangenehmen Situationen führt. Schlimmer noch, sie vermitteln manchmal ein falsches Gefühl der Kompilierzeitsicherheit, indem sie Probleme nicht erkennen, die sich in ihren eigenen Implementierungen manifestieren. Halten Sie sich an das sehr Einfache und vermeiden Sie alles, was wie eine Sprache aussieht, die nicht Java ist (was Sie ursprünglich schreiben wollten).

Weiterführende Lektüre

Diese kurze Liste ist eine Mischung aus überwiegend kritischen (mit Optimismus!) Quellen aus StackOverflow und dem Internet, die meiner Meinung nach einige meiner Punkte veranschaulichen.

In keiner bestimmten Reihenfolge:

Und nach all dem ist mir gerade aufgefallen, dass ich Ihre ursprüngliche Frage möglicherweise immer noch nicht vollständig beantwortet habe 🙂

  • Dies ist zwar eine lohnende Diskussion, aber dieser Beitrag Auch beantwortet nicht die Frage nach dem Wie @Contract funktioniert. Im Gegensatz zu den anderen Antworten wird die eigentlich gestellte Frage überhaupt nicht angesprochen. Ich würde vorschlagen, daraus einen Blogbeitrag oder so etwas zu machen.

    – Matthäus Lesen

    1. März 2017 um 20:21 Uhr

  • @MatthewRead Stimmt, aber das habe ich bereits erwähnt … siehe die letzte Zeile meines Beitrags. Mein Punkt war auch, dass die Frage eine tiefere Diskussion erfordert und keine einfache Antwort hat.

    – Verschränkte Schleifen

    2. März 2017 um 20:56 Uhr

1100110cookie-check@Contract-Anmerkung von JetBrains

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy