ตั้งแต่JSR 305 (ซึ่งมีเป้าหมายคือมาตรฐาน@NonNullและ@Nullable) อยู่เฉยๆเป็นเวลาหลายปีฉันกลัวว่าจะไม่มีคำตอบที่ดี ทั้งหมดที่เราสามารถทำได้คือการหาวิธีแก้ปัญหาในทางปฏิบัติและของฉันเป็นดังนี้:
วากยสัมพันธ์
จากจุดยืนโวหารล้วนๆฉันต้องการหลีกเลี่ยงการอ้างอิงใด ๆ กับ IDE, กรอบงานหรือชุดเครื่องมือใด ๆ ยกเว้นจาวาเอง
กฎนี้ออก:
android.support.annotation
edu.umd.cs.findbugs.annotations
org.eclipse.jdt.annotation
org.jetbrains.annotations
org.checkerframework.checker.nullness.qual
lombok.NonNull
ซึ่งทำให้เรามีอย่างใดอย่างหนึ่งหรือjavax.validation.constraints javax.annotationอดีตมาพร้อมกับ JEE หากสิ่งนี้ดีกว่าjavax.annotationซึ่งอาจมาพร้อมกับ JSE ในที่สุดหรือไม่เคยเป็นเรื่องของการอภิปราย ฉันชอบเป็นการส่วนตัวjavax.annotationเพราะฉันไม่ชอบการพึ่งพา JEE
สิ่งนี้ทำให้เรามี
javax.annotation
ซึ่งก็เป็นอันที่สั้นที่สุดเช่นกัน
มีเพียงหนึ่งไวยากรณ์ที่ดียิ่งขึ้น: java.annotation.Nullable. เป็นแพ็คเกจอื่นจบการศึกษาจากjavaxไปjavaในอดีต javax.annotation จะก้าวไปในทิศทางที่ถูกต้อง
การดำเนินงาน
ฉันหวังว่าพวกเขาทุกคนต่างก็มีการนำไปใช้ที่ไม่สำคัญเหมือนกัน แต่จากการวิเคราะห์อย่างละเอียดแสดงให้เห็นว่านี่ไม่เป็นความจริง
ที่หนึ่งสำหรับความคล้ายคลึงกัน:
@NonNullคำอธิบายประกอบทุกคนมีเส้น
public @interface NonNull {}
ยกเว้น
org.jetbrains.annotationsซึ่งเรียกมัน@NotNullและมีการใช้งานเล็กน้อย
javax.annotation ซึ่งมีการนำไปใช้อีกต่อไป
javax.validation.constraintsซึ่งเรียกมัน@NotNullและมีการดำเนินการ
@Nullableคำอธิบายประกอบทุกคนมีเส้น
public @interface Nullable {}
ยกเว้นสำหรับ (อีกครั้ง) org.jetbrains.annotationsด้วยการใช้งานเล็กน้อย
สำหรับความแตกต่าง:
สิ่งหนึ่งที่น่าทึ่งก็คือ
javax.annotation
javax.validation.constraints
org.checkerframework.checker.nullness.qual
ทั้งหมดมีหมายเหตุประกอบแบบรันไทม์ ( @Retention(RUNTIME)) ขณะที่
android.support.annotation
edu.umd.cs.findbugs.annotations
org.eclipse.jdt.annotation
org.jetbrains.annotations
เป็นเพียงการรวบรวมเวลา (@Retention(CLASS) )
ดังที่อธิบายไว้ในSO นี้คำตอบผลกระทบของหมายเหตุประกอบแบบรันไทม์อาจน้อยกว่าที่คิด แต่มีประโยชน์ในการเปิดใช้งานเครื่องมือเพื่อทำการตรวจสอบรันไทม์นอกเหนือจากเวลารวบรวม
ข้อแตกต่างที่สำคัญอีกประการหนึ่งก็คือตำแหน่งที่สามารถใช้คำอธิบายประกอบในรหัสได้ มีสองวิธีที่แตกต่างกัน บางแพ็คเกจใช้บริบทสไตล์ JLS 9.6.4.1 ตารางต่อไปนี้ให้ภาพรวม:
วิธีฟิลด์พารามิเตอร์ LOCAL_VARIABLE
android.support.annotation XXX
edu.umd.cs.findbugs.annotations XXXX
XXXX. org.jetbrains.annotation
lombok XXXX
javax.validation.constraints XXX
org.eclipse.jdt.annotation, javax.annotationและorg.checkerframework.checker.nullness.qualใช้บริบทที่กำหนดไว้ใน JLS 4.11 ซึ่งอยู่ในความคิดของฉันวิธีการที่เหมาะสมที่จะทำมัน
สิ่งนี้ทำให้เรามี
javax.annotation
org.checkerframework.checker.nullness.qual
ในรอบนี้
รหัส
เพื่อช่วยให้คุณสามารถเปรียบเทียบรายละเอียดเพิ่มเติมด้วยตัวเองฉันจะแสดงรายการรหัสของคำอธิบายประกอบด้านล่างทุกรายการ เพื่อให้การเปรียบเทียบง่ายขึ้นฉันได้ลบความคิดเห็นการนำเข้าและ@Documentedคำอธิบายประกอบ (พวกเขาทั้งหมด@Documentedยกเว้นคลาสจากแพ็คเกจ Android) ฉันจัดเรียงบรรทัดและ@Targetฟิลด์ใหม่และปรับคุณสมบัติให้เป็นมาตรฐาน
package android.support.annotation;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER})
public @interface NonNull {}
package edu.umd.cs.findbugs.annotations;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NonNull {}
package org.eclipse.jdt.annotation;
@Retention(CLASS)
@Target({ TYPE_USE })
public @interface NonNull {}
package org.jetbrains.annotations;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NotNull {String value() default "";}
package javax.annotation;
@TypeQualifier
@Retention(RUNTIME)
public @interface Nonnull {
When when() default When.ALWAYS;
static class Checker implements TypeQualifierValidator<Nonnull> {
public When forConstantValue(Nonnull qualifierqualifierArgument,
Object value) {
if (value == null)
return When.NEVER;
return When.ALWAYS;
}
}
}
package org.checkerframework.checker.nullness.qual;
@Retention(RUNTIME)
@Target({TYPE_USE, TYPE_PARAMETER})
@SubtypeOf(MonotonicNonNull.class)
@ImplicitFor(
types = {
TypeKind.PACKAGE,
TypeKind.INT,
TypeKind.BOOLEAN,
TypeKind.CHAR,
TypeKind.DOUBLE,
TypeKind.FLOAT,
TypeKind.LONG,
TypeKind.SHORT,
TypeKind.BYTE
},
literals = {LiteralKind.STRING}
)
@DefaultQualifierInHierarchy
@DefaultFor({TypeUseLocation.EXCEPTION_PARAMETER})
@DefaultInUncheckedCodeFor({TypeUseLocation.PARAMETER, TypeUseLocation.LOWER_BOUND})
public @interface NonNull {}
เพื่อความสมบูรณ์นี่คือการ@Nullableใช้งาน:
package android.support.annotation;
@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD})
public @interface Nullable {}
package edu.umd.cs.findbugs.annotations;
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
@Retention(CLASS)
public @interface Nullable {}
package org.eclipse.jdt.annotation;
@Retention(CLASS)
@Target({ TYPE_USE })
public @interface Nullable {}
package org.jetbrains.annotations;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface Nullable {String value() default "";}
package javax.annotation;
@TypeQualifierNickname
@Nonnull(when = When.UNKNOWN)
@Retention(RUNTIME)
public @interface Nullable {}
package org.checkerframework.checker.nullness.qual;
@Retention(RUNTIME)
@Target({TYPE_USE, TYPE_PARAMETER})
@SubtypeOf({})
@ImplicitFor(
literals = {LiteralKind.NULL},
typeNames = {java.lang.Void.class}
)
@DefaultInUncheckedCodeFor({TypeUseLocation.RETURN, TypeUseLocation.UPPER_BOUND})
public @interface Nullable {}
แพ็คเกจสองรายการต่อไปนี้ไม่มี@Nullableดังนั้นฉันจึงแยกรายการเหล่านั้นแยกกัน @NonNullลอมบอกมีสวยน่าเบื่อ ในjavax.validation.constraintsความ@NonNullเป็นจริง@NotNull
และมันก็มีการใช้งานที่ยาวนาน
package lombok;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NonNull {}
package javax.validation.constraints;
@Retention(RUNTIME)
@Target({ FIELD, METHOD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Constraint(validatedBy = {})
public @interface NotNull {
String message() default "{javax.validation.constraints.NotNull.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default {};
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@interface List {
NotNull[] value();
}
}
สนับสนุน
จากประสบการณ์ของฉันjavax.annotationอย่างน้อย Eclipse ได้รับการสนับสนุนและ Checker Framework นอกกรอบ
สรุป
บันทึกย่อในอุดมคติของฉันจะเป็นjava.annotationไวยากรณ์ที่มีการใช้งาน Checker Framework
หากคุณไม่ต้องการใช้ Checker Framework javax.annotation( JSR-305 ) ก็ยังเป็นทางออกที่ดีที่สุดของคุณในขณะนี้
org.checkerframework.checker.nullness.qualหากคุณมีความเต็มใจที่จะซื้อเป็นตัวตรวจสอบกรอบเพียงแค่ใช้ของพวกเขา
แหล่งที่มา
android.support.annotation จาก android-5.1.1_r1.jar
edu.umd.cs.findbugs.annotations จาก findbugs-annotations-1.0.0.jar
org.eclipse.jdt.annotation จาก org.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar
org.jetbrains.annotations จาก jetbrains-annotations-13.0.jar
javax.annotation จาก gwt-dev-2.5.1-sources.jar
org.checkerframework.checker.nullness.qual จาก checker-framework-2.1.9.zip
lombokจากการlombokกระทำf6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
javax.validation.constraints จาก validation-api-1.0.0.GA-sources.jar