0
0
Lập trình
Admin Team
Admin Teamtechmely

Giải Quyết Lỗi Tỷ Đô: An Toàn Null Trong Java Với JSpecify

Đăng vào 2 tuần trước

• 6 phút đọc

Giới thiệu

Nếu bạn là một lập trình viên mới bắt đầu với Java hoặc là một kỹ sư dày dạn kinh nghiệm, lỗi thường gặp nhất mà bạn gặp phải chắc chắn là NullPointerException (NPE).

Theo thống kê, NullPointerException là lỗi phần mềm phổ biến thứ hai, cho thấy rất nhiều lập trình viên gặp khó khăn với các trường hợp NPE.

Tony Hoare đã nổi tiếng gọi đây là "lỗi tỷ đô của ông ấy".

"Tôi gọi đây là lỗi tỷ đô của mình. Nó là sự phát minh của tham chiếu null vào năm 1965. ... Điều này đã dẫn đến vô số lỗi, lỗ hổng và sự cố hệ thống, có lẽ đã gây ra một tỷ đô la tổn thất và thiệt hại trong bốn mươi năm qua."

Để cải thiện an toàn null, nhiều nỗ lực đã được thực hiện trong hệ sinh thái Java. Sau Optional và chú thích @NonNull từ JSR 305, JSpecify đã xuất hiện như một tiêu chuẩn hiện đại.

Bài viết này sẽ trình bày cách sử dụng JSpecify để nâng cao sự ổn định của dự án của bạn và lý do tại sao đây là một lựa chọn có chi phí thấp cho cả hệ thống mới và hiện có.

NullPointerException: Tại Sao Vẫn Là Vấn Đề?

Hãy xem đoạn mã sau, gọi một API để trích xuất một token và sử dụng kết quả mà không kiểm tra null.

java Copy
// TokenExtractor.java
public interface TokenExtractor {
    String extractToken(String authorization);
}

// Main.java
TokenExtractor tokenExtractor = new DefaultTokenExtractor();
String token = tokenExtractor.extractToken("some-auth-header");
System.out.println("Chiều dài token: " + token.length()); // <-- NullPointerException!

Mã này sẽ ném ra một NullPointerException nếu phương thức extractToken trả về null. Khi đó, biến token trở thành null và bất kỳ nỗ lực nào để truy cập các thành viên của nó, như token.length(), sẽ kích hoạt ngoại lệ.

Vấn đề cơ bản trong Java là tính khả dụng của null là ngầm định. Trừ khi được nêu rõ trong tài liệu API, lập trình viên không thể chắc chắn liệu giá trị trả về có thể null hay không, dẫn đến những hiểu lầm và lỗi.

JSpecify: Một Tiêu Chuẩn cho An Toàn Null Rõ Ràng

Để giải quyết vấn đề này, các nhóm từ Google, JetBrains, Spring và các tổ chức khác đã hợp tác để tạo ra tiêu chuẩn JSpecify.

JSpecify không chỉ là một tập hợp các chú thích; nó cung cấp một thông số rõ ràng cho an toàn null, đảm bảo hành vi nhất quán trên các công cụ như IDE và trình phân tích tĩnh.

JSpecify định nghĩa tính khả dụng của null trong ba trạng thái:

  1. Chưa xác định: Trạng thái mặc định trong Java, nơi một giá trị có thể là null hoặc không.
  2. Nullable (@Nullable): Rõ ràng cho biết rằng một giá trị có thể là null.
  3. Non-null (@NonNull): Đảm bảo rằng một giá trị sẽ không bao giờ là null.

Thêm Phụ Thuộc JSpecify

groovy Copy
implementation 'org.jspecify:jspecify:1.0.0'

Bây giờ, chúng ta có thể chú thích giao diện TokenExtractor để làm cho tính khả dụng của nó rõ ràng.

java Copy
import org.jspecify.annotations.Nullable;

public interface TokenExtractor {

    @Nullable // Chỉ rõ rằng giá trị trả về có thể là null
    String extractToken(String authorization);
}

Với thay đổi này, một IDE như IntelliJ IDEA sẽ cảnh báo bạn về khả năng xảy ra NullPointerException tại cuộc gọi token.length(), giúp bạn khắc phục sự cố trước khi nó trở thành lỗi thời gian chạy.

Cải Thiện Tính Đọc Được: Thiết Lập Mặc Định Với @NullMarked

Trong hầu hết các API (khoảng 90% thời gian), các giá trị được kỳ vọng là không null. Thêm @NonNull vào mọi tham số và kiểu trả về là một công việc tẻ nhạt, làm rối mã và làm giảm tính đọc được.

Để giải quyết vấn đề này, JSpecify cung cấp chú thích @NullMarked.

Bằng cách áp dụng @NullMarked ở cấp độ gói (trong tệp package-info.java), tất cả các kiểu trong gói đó sẽ được coi là không null theo mặc định.

java Copy
// src/main/java/com/example/package-info.java
@NullMarked
package com.example;

import org.jspecify.annotations.NullMarked;

Bây giờ, mọi thứ được coi là không null trừ khi được đánh dấu rõ ràng với @Nullable, dẫn đến mã sạch hơn và dễ quản lý hơn.

Xác Minh Thời Gian Xây Dựng: Tăng Cường An Toàn Null Với NullAway

Cảnh báo từ IDE rất hữu ích, nhưng chúng không thể ngăn lập trình viên cam kết mã mà phớt lờ chúng.

Để thực thi an toàn null, bạn có thể sử dụng một công cụ phân tích tĩnh như NullAway. NullAway là một plugin Error Prone phân tích các chú thích JSpecify trong quá trình xây dựng và sẽ làm cho việc xây dựng thất bại nếu phát hiện bất kỳ vi phạm an toàn null nào.

Bạn có thể cấu hình nó trong Gradle như sau:

groovy Copy
plugins {
  id 'net.ltgt.errorprone' version '4.1.0'
}

dependencies {
    implementation 'org.jspecify:jspecify:1.0.0'

    errorprone "com.google.errorprone:error_prone_core:2.37.0"
    errorprone "com.uber.nullaway:nullaway:0.12.6"
}

tasks.withType(JavaCompile).configureEach {
    options.errorprone {
        disableAllChecks = true
        option("NullAway:OnlyNullMarked", "true")
        error("NullAway")
    }
}

Nếu bạn phớt lờ cảnh báo từ IDE và chạy xây dựng, nó sẽ thất bại với một lỗi biên dịch, ngăn chặn mã vi phạm an toàn null được triển khai.

JSpecify Trong Hệ Sinh Thái Spring

Bắt đầu với Spring Framework 7 (bao gồm trong Spring Boot 4), toàn bộ mã nguồn đã được chuyển đổi để sử dụng các chú thích JSpecify. Điều này có nghĩa là các lập trình viên Spring có thể tận dụng thông tin an toàn null từ các API của Spring trực tiếp trong IDE và công cụ xây dựng của họ mà không cần bất kỳ cấu hình bổ sung nào.

Ví dụ, phương thức RestClient.body() trả về một @Nullable String, nhắc nhở lập trình viên xử lý khả năng phản hồi null một cách thích hợp.

java Copy
// API RestClient của Spring
@Nullable
String body = restClient.get().uri("/user").retrieve().body(String.class);

// IDE cảnh báo rằng 'body' có thể là null, nhắc nhở kiểm tra null.
System.out.println(body.length());

Tương Lai Của An Toàn Null Trong Java

Trong dài hạn, các tính năng an toàn null được lên kế hoạch cho ngôn ngữ Java như một phần của Dự án Valhalla.

Cú pháp mới như String? (nullable) và String! (non-null) đã được đề xuất. Tuy nhiên, do cam kết của Java về khả năng tương thích ngược, mặc định cho các loại không được chú thích sẽ có khả năng vẫn là "chưa xác định". (https://openjdk.org/jeps/8303099)

Vì tính năng này vẫn đang ở giai đoạn đề xuất và có khả năng sẽ mất vài năm để hiện thực hóa, JSpecify và NullAway hiện đại đại diện cho cách thực tiễn và mạnh mẽ nhất để cải thiện sự ổn định của các ứng dụng Java.

Kết luận

JSpecify và NullAway là sự kết hợp mạnh mẽ để giải quyết "lỗi tỷ đô" của Java. Bằng cách sử dụng các chú thích rõ ràng, bạn làm rõ ý định của mã và có thể loại bỏ khả năng xảy ra NullPointerExceptions tại thời điểm biên dịch thông qua việc tích hợp IDE và công cụ xây dựng.

Dựa trên kinh nghiệm của tôi khi giới thiệu JSpecify cho nhóm của mình, tôi đã thấy tận mắt cách một cấu hình đơn giản có thể cải thiện đáng kể sự ổn định của ứng dụng và chất lượng mã. Một lợi thế lớn là khả năng áp dụng nó dần dần theo từng gói, điều này làm cho việc áp dụng nó vào các dự án hiện có trở nên dễ dàng hơn.

Tôi khuyến khích bạn thử JSpecify trong các dự án của mình và bắt đầu viết mã Java an toàn, mạnh mẽ hơn ngay hôm nay.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào