NullAway เป็นเครื่องมือที่ช่วยกำจัด NullPointerException
s (NPE) ในโค้ด Java ของคุณ หากต้องการใช้ NullAway ขั้นแรกให้เพิ่มคำอธิบายประกอบ @Nullable
ในโค้ดของคุณไม่ว่าฟิลด์ พารามิเตอร์เมธอด หรือค่าที่ส่งคืนอาจเป็น null
ด้วยคำอธิบายประกอบเหล่านี้ NullAway จะดำเนินการชุดการตรวจสอบตามประเภทและในเครื่องเพื่อให้แน่ใจว่าตัวชี้ใดๆ ที่ได้รับการยกเลิกการอ้างอิงในโค้ดของคุณต้องไม่เป็น null
NullAway คล้ายกับการตรวจสอบ nullability ตามประเภทในภาษา Kotlin และ Swift และ Checker Framework และ Eradicate null checkers สำหรับ Java
NullAway นั้น รวดเร็ว มันถูกสร้างขึ้นเป็นปลั๊กอินสำหรับ Error Prone และสามารถทำงานบนโค้ดของคุณทุก ๆ บิลด์ ในการวัดของเรา ค่าใช้จ่ายในการรัน NullAway ในเวลาการสร้างมักจะน้อยกว่า 10% NullAway ใช้งานได้จริง เช่นกัน โดยไม่ได้ป้องกัน NPE ที่เป็นไปได้ทั้งหมดในโค้ดของคุณ แต่จะจับ NPE ส่วนใหญ่ที่เราสังเกตเห็นในการผลิต ขณะเดียวกันก็กำหนดภาระคำอธิบายประกอบที่สมเหตุสมผล ซึ่งให้ "ผลตอบแทนที่คุ้มค่า" อย่างมาก
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
ที่สองตั้งค่าเวอร์ชันของ Error Prone ที่ใช้
สุดท้ายนี้ ในส่วน tasks.withType(JavaCompile)
เราจะส่งตัวเลือกการกำหนดค่าบางอย่างไปยัง NullAway check("NullAway", CheckSeverity.ERROR)
ตั้งค่าปัญหา NullAway ไปที่ระดับข้อผิดพลาด (เทียบเท่ากับอาร์กิวเมนต์ Error Prone มาตรฐาน -Xep:NullAway:ERROR
) โดยค่าเริ่มต้น NullAway จะส่งเสียงคำเตือน จากนั้น option("NullAway:AnnotatedPackages", "com.uber")
(เทียบเท่ากับ -XepOpt:NullAway:AnnotatedPackages=com.uber
อาร์กิวเมนต์ Error Prone มาตรฐาน) จะบอก NullAway ว่าซอร์สโค้ดในแพ็คเกจภายใต้เนมสเปซ com.uber
ควรเป็น ตรวจสอบการอ้างอิงแบบ null และการใช้งานที่เหมาะสมของคำอธิบายประกอบ @Nullable
และไฟล์คลาสในแพ็คเกจเหล่านี้ควรถือว่ามีการใช้งานที่ถูกต้อง ของ @Nullable
(ดูเอกสารสำหรับรายละเอียดเพิ่มเติม) NullAway ต้องการอย่างน้อยอาร์กิวเมนต์การกำหนดค่า AnnotatedPackages
จึงจะทำงาน เพื่อแยกความแตกต่างระหว่างโค้ดที่มีคำอธิบายประกอบและที่ไม่มีคำอธิบายประกอบ ดูเอกสารการกำหนดค่าสำหรับตัวเลือกการกำหนดค่าที่มีประโยชน์อื่นๆ สำหรับการกำหนดค่าตัวเลือก NullAway ที่ง่ายยิ่งขึ้น ให้ใช้ปลั๊กอิน Gradle NullAway สุดท้ายนี้ เราจะแสดงวิธีปิดการใช้งาน NullAway ในโค้ดทดสอบ หากต้องการ
เราขอแนะนำให้แก้ไขปัญหาทั้งหมดที่รายงานข้อผิดพลาดได้ง่าย โดยเฉพาะปัญหาที่รายงานว่าเป็นข้อผิดพลาด (แทนที่จะเป็นคำเตือน) แต่หากคุณต้องการทดลองใช้ NullAway โดยไม่ต้องดำเนินการตรวจสอบ Error Prone อื่นๆ คุณสามารถใช้ options.errorprone.disableAllChecks
(เทียบเท่ากับการส่ง "-XepDisableAllChecks"
ไปยังคอมไพเลอร์ ก่อนอาร์กิวเมนต์เฉพาะของ NullAway)
Gradle Error Prone Plugin เวอร์ชัน 3.0.0 และใหม่กว่าไม่รองรับ Android อีกต่อไป ดังนั้นหากคุณใช้ปลั๊กอินเวอร์ชันล่าสุด คุณจะต้องเพิ่มการกำหนดค่าเพิ่มเติมเพื่อเรียกใช้ Error Prone และ NullAway ไฟล์ build.gradle
ของแอปตัวอย่างของเราแสดงวิธีหนึ่งในการดำเนินการนี้ แต่โปรเจ็กต์ Android ของคุณอาจต้องได้รับการปรับแต่ง อีกทางหนึ่ง Gradle Error Prone Plugin เวอร์ชัน 2.x ยังคงรองรับ 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 เพื่อแก้ไขปัญหา
ซึ่งแตกต่างจากตัวประมวลผลคำอธิบายประกอบอื่นๆ ข้างต้น ลอมบอกปรับเปลี่ยน AST ในหน่วยความจำของโค้ดที่ประมวลผล ซึ่งเป็นที่มาของความเข้ากันไม่ได้หลายอย่างกับ Error Prone และผลที่ตามมาคือ NullAway
เราไม่แนะนำให้ใช้ NullAway กับลอมบอกเป็นพิเศษ อย่างไรก็ตาม NullAway เข้ารหัสความรู้บางอย่างเกี่ยวกับคำอธิบายประกอบทั่วไปของลอมบอก และเราพยายามใช้ความเข้ากันได้อย่างดีที่สุด โดยเฉพาะอย่างยิ่ง การใช้งานทั่วไป เช่น คลาส @lombok.Builder
และ @Data
ควรได้รับการสนับสนุน
เพื่อให้ NullAway ตรวจพบโค้ดที่สร้างใน Lombok ภายใน Java AST ในหน่วยความจำได้สำเร็จ ต้องส่งตัวเลือกการกำหนดค่าต่อไปนี้ไปยัง Lombok โดยเป็นส่วนหนึ่งของไฟล์ lombok.config
ที่เกี่ยวข้อง:
lombok.addLombokGeneratedAnnotation = true
สิ่งนี้ทำให้ลอมบอกเพิ่ม @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 สำหรับข้อมูลเพิ่มเติม