首頁  >  文章  >  Java  >  為什麼我認為 Lombok 應該從 Java 專案中丟棄

為什麼我認為 Lombok 應該從 Java 專案中丟棄

PHPz
PHPz原創
2024-09-03 14:01:08658瀏覽

您好,今天的文章解決了一個看似不受歡迎的觀點,我相信它會遇到一些阻力。某件事在技術上可行並不能自動認可其實用性或適用性。因此,我將嘗試證實​​為什麼我相信使用 Lombok 可能會對您的程式碼產生不利影響。

揭開魔力:了解 Lombok 項目

在深入研究不太流行的細節之前,讓我先簡單解釋一下 Lombok 庫的功能。

Project Lombok 充當一個函式庫,在編譯時將程式碼注入到類別中,這看起來幾乎很神奇。要理解它的操作,了解Java編譯過程是不可或缺的。 Java編譯主要分為三個階段(圖1):解析與輸入、註解處理、分析與生成,如下圖:

Why I Believe Lombok Should Be Discarded from Java Projects

圖 1 – 抽象語法樹 (AST)

  1. 解析並輸入:
    在這裡,編譯器將原始檔轉換為抽象語法樹(AST)。僅因語法無效而引發錯誤,而不會因錯誤的類別或方法使用而引發錯誤。

  2. 註解處理:
    在此階段,自訂註解處理器會驗證類別或產生新資源(例如原始檔案)。如果產生新的來源,這可能會觸發新的編譯週期。

  3. 分析並產生:
    在最後階段,編譯器從 AST 產生字節碼,檢查損壞的引用,驗證邏輯流,執行類型擦除,並對語法糖進行脫糖。

Project Lombok 作為註解處理器運行,透過注入新方法、欄位或表達式來修改 AST。與產生新來源的典型處理器不同,Lombok 會更改現有類,這一區別使其能夠直接影響生成的字節碼。

J2SE 1.5 中引入的 AnnotationProcessor 無法變更現有檔案。它只能建立新檔案或字節碼。這使得 lombok 的實作很有趣,因為它們在編譯階段使用 AnnotationProcessor 來修改現有的 java 類別檔案。這是 的概述 使用 Lombok 的編譯過程(
圖 2)。

Why I Believe Lombok Should Be Discarded from Java Projects

圖2 – 編譯過程與Lombok

針對龍目島的案件

了解了 Lombok 背後的魔力後,讓我們深入探討一下我認為它可能對您的程式碼庫有害的原因。

增加編譯時間

Lombok 在編譯時的操作不可避免地會延長編譯過程,特別是在較大的程式碼庫中,由於需要增加 AST 管理,這一點尤其明顯。

利益錯位

Lombok 提供了各種註釋,可能會給人一種解決基本程式設計挑戰的錯覺。我將討論我在程式碼庫中經常遇到的一些註釋。

    @Builder註解
Lombok 中的 @Builder 註解透過建構器模式簡化了物件創建,增加了一層最初吸引人的便利性。考慮這個例子:


@Data
@Builder
public class Course {
    public enum Type {
        ONLINE,
        ONSITE;

        @JsonValue
        @Override
        public String toString() {
            return super.toString().toLowerCase();
        }
    }

    private long id;
    private Type type;
}
及其用法:


public class CourseCreator {

    public static Course createCourse(Enrollment enrollment, Registration registration) {
        Course.Type courseType = enrollment.getVenue().equals(registration.getVenue()) ? Course.Type.ONSITE : Course.Type.ONLINE;

        return Course.builder()
            .id(enrollment.getId())
            .type(courseType)
            .build();
    }

     public static void main(String[] args) {

        Registration registration = new Registration(); 
        Enrollment enrollment = new Enrollment();
        Course course = createCourse(enrollment, registration);
        System.out.println(course);
    }
}
雖然構建器模式得到了有效的實現,但也引發了有關所創建物件的完整性和有效性的關鍵問題。

如果我們在建構器中省略 .type(),我們將實例化什麼類型的課程?

這行程式碼可以編譯,但它讓我們產生疑問:我們實際上創建了什麼類型的課程?這是有效的課程實例嗎?


Course.builder().id(1L).build();
這些擔憂表明,開發人員可能會受到註釋便利性的影響,可能會忽略維護業務邏輯完整性所需的徹底的領域建模。不要讓 Lombok 決定我們的設計,更重要的是確保與業務需求一致的深思熟慮的方法。

考慮調整實施,以確保任何課程創建都是明確的並受到業務環境的約束:


@Data
public class Course {
    private enum Type {
        ONLINE,
        ONSITE;

        @JsonValue
        @Override
        public String toString() {
            return super.toString().toLowerCase();
        }
    }

    public static Course online(long id) {
        return new Course(id, Type.ONLINE);
    }

    public static Course onsite(long id) {
        return new Course(id, Type.ONSITE);
    }

    private long id;
    private Type type;

    public boolean isOnline() {
        return Type.ONLINE.equals(this.type);
    }

    public boolean isOnsite() {
        return Type.ONSITE.equals(this.type);
    }
}
透過重新設計類別:


public class CourseManagement {
    public static Course createAppropriateCourse(Enrollment enrollment, Registration registration) {
        return enrollment.getVenue().equals(registration.getVenue()) ? 
            Course.onsite(enrollment.getId()) : 
            Course.online(enrollment.getId());
    }

    public static void main(String[] args) {
        Registration registration = new Registration();
        Enrollment enrollment = new Enrollment();
        Course createdCourse = createAppropriateCourse(enrollment, registration);

        System.out.println(createdCourse);
    }
}
修改後的設計確保了 Course 物件的創建是明確且萬無一失的,反映了領域固有的受限選擇並消除了歧義。

此外,透過將 Type 枚舉設為私有並提供清晰、顯式的方法(如 isOnline() 和 isOnsite()),我們確保僅公開和操作有效狀態,從而保護網域完整性。

Through this thoughtful restructuring, we demonstrate that while tools like Lombok can significantly reduce boilerplate, they are not substitutes for careful design and a deep understanding of the domain. It underscores that Lombok should be employed judiciously, complementing rather than overshadowing robust architectural practices. This ensures that the elegance of our code does not come at the expense of its correctness and clarity.

  • Overreliance on @Setter and @Getter

The argument that getters and setters reduce boilerplate falls short when Java offers alternatives like the Record classes from Java 14.

@Data
public class Movie {
    private String title;
    private int releaseYear;
}

// Can be replaced with:
public record Movie(String title, int releaseYear) {}
  • Superintendent @NonNull

Having null in your code - aside from inputs is generally considered problematic and is often indicative of deeper design issues. The prevalent advice is to avoid returning null whenever possible. Instead, opt for alternatives such as returning non-null collections, utilizing null objects, or throwing exceptions to signify unusual or exceptional conditions. This strategic avoidance means null checks become redundant in most parts of your code.

To distance from Lombok's @NonNull annotation and ensure robustness in Java natively, the Objects.requireNonNull() method from the java.util.Objects class is incredibly useful.
This method streamlines null checking by ensuring that an object is not null, and it throws a NullPointerException with a clear message if it is. This explicit exception-throwing mechanism prevents latent null-related bugs from surfacing in runtime, promoting earlier detection during the development cycle. Here’s an example showing how this method can replace Lombok's functionality

Using Lombok's@NonNull:

public class NonNullExample {
    private Student student;

    public NonNullExample(@NonNull Student student) {
        this.student = student;
    }
}

Equivalent pure Java approach:

import java.util.Objects;

public class NonNullExample {
    private Student student;

    public NonNullExample(Student student) {
        this.student = Objects.requireNonNull(student, "Student cannot be null");
    }
}

This transition to native Java handling enhances code transparency by making the null-check explicit, which is advantageous for code maintenance and understanding.

Constructor Flexibility and Reusability

Constructors play a critical role in how classes interact within your software architecture. A well-designed class should have a variety of constructors that accommodate different use cases, promoting reusability and flexibility. If your constructors merely replicate field assignments, the underlying issue isn't the need to write boilerplate code; rather, it's the risk of fostering a non-reusable and inflexible design that Lombok cannot rectify. Proper constructor design allows a class to be integrated and utilized in a multitude of scenarios, enhancing the overall robustness and adaptability of your codebase.

Evaluating Boilerplate Code: The Lure of Lombok versus Modern Java Features

Lombok's popularity predominantly stems from its ability to reduce boilerplate code, particularly in domain-specific classes like transfer and data objects. While Lombok effectively diminishes the visible clutter by auto-generating necessary code like getters, setters, equals, hashCode, and toString methods, this convenience might obscure potential pitfalls. However, with the advent of Java Records introduced in Java 14, there is a preferable alternative that natively supports the concise declaration of immutable data carriers. Most integrated
development environments (IDEs) are also equipped to automatically generate these boilerplate codes with minimal user input, offering a balance between Lombok’s automation and the control of traditional Java coding.

Compatibility Concerns

Project Lombok's dependency on the underlying Java version poses a significant compatibility risk. As Java evolves, the Abstract Syntax Tree (AST) structure and its interpretation could change, necessitating continuous updates to Lombok to ensure compatibility. This creates a fragile dependency where upgrading to a newer Java version could potentially break your build if Lombok is not simultaneously updated to support these changes. The reliance on unofficial or private APIs to modify class definitions further exacerbates this issue because these APIs could be restricted or altered in future Java releases, threatening Lombok’s long-term viability.

Java 標準

使用僅使用標準 Java 編譯器選項的工具建立專案時,使用 Lombok 可能會導致複雜化。例如,如果您的程式碼使用 Lombok 產生的 getter 和 setter,則直接使用 javac 進行編譯而不進行 Lombok 預處理可能會導致錯誤,指示缺少方法。雖然有些人可能認為 Lombok 注入程式碼的能力是一個聰明的「技巧」,但批判性地評估相關風險和替代方案至關重要。問題的核心在於Java的註解處理規格並不正式支援在編譯期間修改現有的類別。依賴這些非官方技術使得 Lombok 容易受到未來 Java 更新的影響,這些更新可能會破壞或停用其功能。

最終,這些考慮因素強調了不僅要評估 Lombok 等工具的直接好處的重要性,還要評估它們對可維護性、相容性以及與 Java 標準的一致性的長期影響。隨著 Java 的不斷發展,對穩定、標準化功能的依賴對於確保軟體專案的可持續性和可靠性變得越來越重要。

結論

Lombok 似乎是 Java 開發的便捷捷徑,但它將 Java 程式碼轉換為特定於域的版本,我喜歡稱之為 「Lombok Java」。必須認識到,過度依賴 Lombok 可能會掩蓋 Java 本質,可能會導致程式碼不夠健壯,並且在沒有 Lombok 的情況下更難以管理。

如果過度依賴 Lombok 是管理程式碼庫的解決方案,那麼可能是時候重新評估底層架構和實踐了。 Java 的真正優勢在於其清晰性和結構,而不是外部程式庫提供的捷徑。

如果有機會,我會選擇從我的專案中放棄 Lombok。

以上是為什麼我認為 Lombok 應該從 Java 專案中丟棄的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn