NullAway هي أداة للمساعدة في إزالة NullPointerException
s (NPEs) في كود Java الخاص بك. لاستخدام NullAway، قم أولاً بإضافة التعليقات التوضيحية @Nullable
في التعليمات البرمجية الخاصة بك حيثما يكون الحقل أو معلمة الطريقة أو القيمة المرجعة null
. بالنظر إلى هذه التعليقات التوضيحية، تقوم NullAway بتنفيذ سلسلة من عمليات التحقق المحلية المستندة إلى النوع للتأكد من أن أي مؤشر يتم إلغاء الإشارة إليه في التعليمات البرمجية الخاصة بك لا يمكن أن يكون null
. NullAway يشبه التحقق من القيمة الخالية على أساس النوع في لغات Kotlin وSwift، وChecker Framework وEradicate null checkers لـ Java.
NullAway سريع . لقد تم تصميمه كمكون إضافي لبرنامج Error Prone ويمكن تشغيله على كل إصدار من التعليمات البرمجية الخاصة بك. في قياساتنا، عادةً ما يكون الحمل الإضافي لوقت البناء لتشغيل NullAway أقل من 10%. يعد NullAway عمليًا أيضًا: فهو لا يمنع جميع NPEs المحتملة في التعليمات البرمجية الخاصة بك، ولكنه يلتقط معظم NPEs التي لاحظناها في الإنتاج مع فرض عبء تعليق توضيحي معقول، مما يوفر "قيمة كبيرة مقابل أموالك".
يتطلب NullAway إنشاء التعليمات البرمجية الخاصة بك باستخدام Error Prone، الإصدار 2.14.0 أو أعلى. راجع وثائق Error Prone للحصول على إرشادات حول بدء استخدام Error Prone والتكامل مع نظام الإصدار الخاص بك. تفترض التعليمات أدناه أنك تستخدم Gradle؛ راجع المستندات لمناقشة أنظمة البناء الأخرى.
لدمج NullAway في مشروع Java غير Android، أضف ما يلي إلى ملف build.gradle
الخاص بك:
plugins {
// we assume you are already using the Java plugin
id " net.ltgt.errorprone " version " <plugin version> "
}
dependencies {
errorprone " com.uber.nullaway:nullaway:<NullAway version> "
// Some source of nullability annotations; JSpecify recommended,
// but others supported as well.
api " org.jspecify:jspecify:1.0.0 "
errorprone " com.google.errorprone:error_prone_core:<Error Prone version> "
}
import net.ltgt.gradle.errorprone.CheckSeverity
tasks . withType( JavaCompile ) {
options . errorprone {
check( " NullAway " , CheckSeverity . ERROR )
option( " NullAway:AnnotatedPackages " , " com.uber " )
}
// Include to disable NullAway on test code
if (name . toLowerCase() . contains( " test " )) {
options . errorprone {
disable( " NullAway " )
}
}
}
دعونا نسير عبر هذا البرنامج النصي خطوة بخطوة. يقوم قسم plugins
بسحب المكون الإضافي Gradle Error Prone لتكامل Error Prone.
في dependencies
، يقوم السطر الأول errorprone
بتحميل NullAway، ويقوم سطر api
بتحميل مكتبة JSpecify التي توفر تعليقات توضيحية مناسبة لقابلية البطلان، على سبيل المثال، org.jspecify.annotations.Nullable
. يسمح NullAway باستخدام أي تعليق توضيحي @Nullable
، لذلك، على سبيل المثال، @Nullable
من مكتبة التعليقات التوضيحية AndroidX أو التعليقات التوضيحية JetBrains يعد أمرًا جيدًا أيضًا. يحدد السطر الثاني من errorprone
إصدار ErrorProne المستخدم.
أخيرًا، في قسم tasks.withType(JavaCompile)
، نقوم بتمرير بعض خيارات التكوين إلى NullAway. check("NullAway", CheckSeverity.ERROR)
بتعيين مشكلات NullAway إلى مستوى الخطأ (وهو ما يعادل -Xep:NullAway:ERROR
وسيطة Error Prone القياسية)؛ بشكل افتراضي، يصدر NullAway تحذيرات. بعد ذلك، فإن option("NullAway:AnnotatedPackages", "com.uber")
(المكافئ لـ -XepOpt:NullAway:AnnotatedPackages=com.uber
) يخبر NullAway أن التعليمات البرمجية المصدر في الحزم ضمن مساحة الاسم com.uber
يجب أن تكون تم التحقق من عدم وجود مرجعيات فارغة والاستخدام الصحيح للتعليقات التوضيحية @Nullable
، وينبغي افتراض أن ملفات الفئة في هذه الحزم لها الاستخدام الصحيح لـ @Nullable
(راجع المستندات لمزيد من التفاصيل). يتطلب NullAway على الأقل وسيطة تكوين AnnotatedPackages
للتشغيل، من أجل التمييز بين التعليمات البرمجية المشروحة وغير المشروحة. راجع مستندات التكوين للتعرف على خيارات التكوين المفيدة الأخرى. للحصول على تكوين أبسط لخيارات NullAway، استخدم البرنامج الإضافي Gradle NullAway. وأخيرًا، نعرض كيفية تعطيل NullAway على رمز الاختبار، إذا رغبت في ذلك.
نوصي بمعالجة جميع المشكلات التي يبلغ عنها Error Prone، وخاصة تلك التي يتم الإبلاغ عنها على أنها أخطاء (بدلاً من التحذيرات). ولكن، إذا كنت ترغب في تجربة NullAway دون تشغيل عمليات التحقق الأخرى من Error Prone، فيمكنك استخدام options.errorprone.disableAllChecks
(أي ما يعادل تمرير "-XepDisableAllChecks"
إلى المترجم، قبل الوسائط الخاصة بـ NullAway).
لم تعد الإصدارات 3.0.0 والإصدارات الأحدث من Gradle Error Prone Plugin تدعم Android. لذا، إذا كنت تستخدم إصدارًا حديثًا من هذا البرنامج الإضافي، فستحتاج إلى إضافة بعض التكوينات الإضافية لتشغيل Error Prone وNullAway. يُظهر نموذج ملف build.gradle
الخاص بتطبيقنا طريقة واحدة للقيام بذلك، ولكن مشروع Android الخاص بك قد يتطلب تعديلات. بدلاً من ذلك، لا تزال إصدارات 2.x من Gradle Error Prone Plugin تدعم Android وقد تستمر في العمل مع مشروعك.
أبعد من ذلك، مقارنة بتكوين Java، يمكن إزالة تبعية JSpecify؛ يمكنك استخدام التعليق التوضيحي androidx.annotation.Nullable
من مكتبة التعليقات التوضيحية AndroidX بدلاً من ذلك.
تقوم بعض معالجات التعليقات التوضيحية، مثل Dagger وAutoValue، بإنشاء تعليمات برمجية في نفس مساحة اسم الحزمة مثل التعليمات البرمجية الخاصة بك. يمكن أن يسبب هذا مشاكل عند ضبط NullAway على مستوى ERROR
كما هو مقترح أعلاه، نظرًا لأن الأخطاء في هذا الكود الذي تم إنشاؤه ستمنع الإنشاء. أفضل حل حاليًا لهذه المشكلة هو تعطيل Error Prone تمامًا على التعليمات البرمجية التي تم إنشاؤها، باستخدام خيار -XepExcludedPaths
المضاف في Error Prone 2.1.3 (موثق هنا، استخدم options.errorprone.excludedPaths=
في Gradle). للاستخدام، اكتشف الدليل الذي يحتوي على الكود الذي تم إنشاؤه، وأضف هذا الدليل إلى المسار المستبعد regex.
ملاحظة لمستخدمي Dagger : يمكن أن تحتوي إصدارات Dagger الأقدم من 2.12 على تفاعلات سيئة مع NullAway؛ انظر هنا. يرجى التحديث إلى Dagger 2.12 لإصلاح المشكلة.
على عكس معالجات التعليقات التوضيحية الأخرى المذكورة أعلاه، يقوم Lombok بتعديل AST الموجود في الذاكرة للتعليمات البرمجية التي يعالجها، وهو مصدر العديد من حالات عدم التوافق مع Error Prone، وبالتالي NullAway.
لا نوصي بشكل خاص باستخدام NullAway مع Lombok. ومع ذلك، تقوم NullAway بتشفير بعض المعرفة بتعليقات Lombok التوضيحية الشائعة ونحاول تحقيق أفضل جهد ممكن للتوافق. على وجه الخصوص، يجب دعم الاستخدامات الشائعة مثل @lombok.Builder
و @Data
Data.
لكي يتمكن NullAway من اكتشاف تعليمات برمجية Lombok التي تم إنشاؤها بنجاح داخل Java AST في الذاكرة، يجب تمرير خيار التكوين التالي إلى Lombok كجزء من ملف lombok.config
القابل للتطبيق:
lombok.addLombokGeneratedAnnotation = true
يؤدي هذا إلى قيام Lombok بإضافة @lombok.Generated
إلى الأساليب/الفئات التي ينشئها. سوف يتجاهل NullAway (أي لن يتحقق) تنفيذ هذه التعليمات البرمجية التي تم إنشاؤها، ويعاملها على أنها غير مشروحة.
دعونا نرى كيف يعمل NullAway بمثال تعليمي بسيط:
static void log ( Object x ) {
System . out . println ( x . toString ());
}
static void foo () {
log ( null );
}
هذا الرمز به أخطاء: عند استدعاء foo()
، سيفشل الاستدعاء اللاحق لـ log()
مع NPE. يمكنك رؤية هذا الخطأ في نموذج تطبيق NullAway عن طريق تشغيل:
cp sample/src/main/java/com/uber/mylib/MyClass.java.buggy sample/src/main/java/com/uber/mylib/MyClass.java
./gradlew build
افتراضيًا، يفترض NullAway أن كل معلمة أسلوب وقيمة إرجاع وحقل غير فارغة ، أي أنه لا يمكن أبدًا تعيين قيمة null
له. في الكود أعلاه، من المفترض أن تكون المعلمة x
log()
غير فارغة. لذلك، أبلغ NullAway عن الخطأ التالي:
warning: [NullAway] passing @Nullable parameter 'null' where @NonNull is required
log(null);
^
يمكننا إصلاح هذا الخطأ عن طريق السماح بتمرير null
إلى log()
، مع تعليق توضيحي @Nullable
:
static void log ( @ Nullable Object x ) {
System . out . println ( x . toString ());
}
باستخدام هذا التعليق التوضيحي، يشير NullAway إلى المرجعية الخالية المحتملة:
warning: [NullAway] dereferenced expression x is @Nullable
System.out.println(x.toString());
^
يمكننا إصلاح هذا التحذير عن طريق إضافة علامة فارغة:
static void log ( @ Nullable Object x ) {
if ( x != null ) {
System . out . println ( x . toString ());
}
}
مع هذا التغيير، تم إصلاح جميع تحذيرات NullAway.
لمزيد من التفاصيل حول عمليات التحقق ورسائل الخطأ والقيود الخاصة بـ NullAway، راجع دليلنا التفصيلي.
لا تتردد في فتح مشكلة GitHub إذا كانت لديك أي أسئلة حول كيفية استخدام NullAway. أو يمكنك الانضمام إلى خادم NullAway Discord وطرح سؤال علينا هناك.
نحن نحب أن تساهم في NullAway! يرجى ملاحظة أنه بمجرد إنشاء طلب سحب، سيُطلب منك التوقيع على اتفاقية ترخيص Uber Contributor الخاصة بنا.
NullAway مرخص بموجب ترخيص MIT. راجع ملف LICENSE.txt لمزيد من المعلومات.