可愛的Lombok

        Lombok項目的主頁鏈接http://projectlombok.org/index.html。

        自打Lombok引入項目之後,就不可救藥的喜歡上了這一款工具。本文記錄了在項目中應用Lombok時的經驗,希望對更多的朋友有幫助。

        如下樣例使用到了0.11.5版本的Lombok,目前最新版本爲1.12.6。

        初學Lombok的使用時,建議配合Java的編譯工具,便於理解Lombok在背後做的事情。

Lombok解決了什麼問題

        沒使用Lombok之前,定義JavaBean時,需要爲每個成員定義getter/setter方法,無論手寫或者自動生成,過程都不麻煩,但當Bean裏的成員蠻多時,看代碼的時候就比較心煩,無關的部分越來越多。隨着項目的成長,代碼的行數也奇蹟般的增長,以至於項目經理在評估工作量時都要考慮適時的調整比率,麻煩呀。

        下面給出一個樣例。

應用Lombok的效果
未使用Lombok 使用Lombok 反編譯Lombok生成的class後得到的代碼
class Person
{
    private String name;
    private int age;
    private String job;
    private Object info;


    public void setInfo(final Object info)
    {
        this.info = info;
    }


    public void setJob(final String job)
    {
        this.job = job;
    }


    public void setAge(final int age)
    {
        this.age = age;
    }


    public void setName(final String name)
    {
        this.name = name;
    }


    public Object getInfo()
    {
        return info;
    }


    public String getJob()
    {
        return job;
    }


    public int getAge()
    {
        return age;
    }


    public String getName()
    {
        return name;
    }
}
@Data
class Person
{
    private String name;
    private int age;
    private String job;
    private Object info;
}
class Person
{
    private String name;
    private int age;
    private String job;
    private Object info;


    @Override
    public String toString()
    {
        return "Person(name=" + getName() + ", age=" + getAge() + ", job=" + getJob() + ", info=" + getInfo() + ")";
    }


    @Override
    public int hashCode()
    {
        final int PRIME = 31;
        int result = 1;
        final Object $name = getName();
        result = result * 31 + ($name == null ? 0 : $name.hashCode());
        result = result * 31 + getAge();
        final Object $job = getJob();
        result = result * 31 + ($job == null ? 0 : $job.hashCode());
        final Object $info = getInfo();
        result = result * 31 + ($info == null ? 0 : $info.hashCode());
        return result;
    }


    public boolean canEqual(final Object other)
    {
        return other instanceof Person;
    }


    @Override
    public boolean equals(final Object o)
    {
        if (o == this)
        {
            return true;
        }
        if (!(o instanceof Person))
        {
            return false;
        }
        final Person other = (Person) o;
        if (!other.canEqual(this))
        {
            return false;
        }
        final Object this$name = getName();
        final Object other$name = other.getName();
        if (this$name == null ? other$name != null : !this$name.equals(other$name))
        {
            return false;
        }
        if (getAge() != other.getAge())
        {
            return false;
        }
        final Object this$job = getJob();
        final Object other$job = other.getJob();
        if (this$job == null ? other$job != null : !this$job.equals(other$job))
        {
            return false;
        }
        final Object this$info = getInfo();
        final Object other$info = other.getInfo();
        return this$info == null ? other$info == null : this$info.equals(other$info);
    }


    public void setInfo(final Object info)
    {
        this.info = info;
    }


    public void setJob(final String job)
    {
        this.job = job;
    }


    public void setAge(final int age)
    {
        this.age = age;
    }


    public void setName(final String name)
    {
        this.name = name;
    }


    public Object getInfo()
    {
        return info;
    }


    public String getJob()
    {
        return job;
    }


    public int getAge()
    {
        return age;
    }


    public String getName()
    {
        return name;
    }
}

        上述應用僅僅是Lombok的一個簡單樣例,但仍然可以說明很多問題。通過在在class關鍵字前增加@Data,Lombok除了自動生成所有非final成員的getter/setter方法外,還有額外的福利,lombok同時生成了toString、equals、hashCode方法,並且完全符合規範,這樣縮減了代碼的規模,也少了手寫toString/equals/hashCode方法的煩惱。

Lombok可以做的更多

@ToString

        在調試代碼時經常遇到一個問題,查看某個變量的值時,發現變量窗口展示的是一個奇怪的值(或者說對象在JVM內部表示的地址,這其實是toString方法的默認實現);想要查看對象內部各成員的值就需要逐層展開,這樣才能看到內部的信息。類似的調試過程煩不勝煩,但對於定義了合適的toString方法的類的對象,調試時查看其內部成員的值則會簡單許多,調試器會自動調用對象的toString方法,並將得到的字符串展示在變量值窗口,這無疑爲調試帶來了莫大便利。但手寫toString方法其實非常麻煩,有過相關經歷的朋友可能會深有體會。對於某個具體的類來說,出於安全或者性能或者其它方面的考慮,可能並不希望全部成員都出現在toString方法的返回值裏。

        Lombok提供了@ToString來滿足上述的需求。下面是使用@ToString的樣例。

@ToString的樣例
樣例 生成的代碼
@ToString(exclude = { "age", "job" }, includeFieldNames = false, doNotUseGetters = true)
class Staff
{
    private String name;
    private int age;
    private String job;
    private Object info;
}

@ToString(callSuper = false)
class Student extends Staff
{
    private String schoolName;
    private int classNo;
}
class Staff
{
    private String name;
    private int age;
    private String job;
    private Object info;

    @Override
    public String toString()
    {
        return "Staff(" + name + ", " + info + ")";
    }
}

class Student extends Staff
{
    private String schoolName;
    private int classNo;

    @Override
    public String toString()
    {
        return "Student(schoolName=" + schoolName + ", classNo=" + classNo + ")";
    }
}


        樣例使用了@ToString提供的如下能力:

  1. 使用exclude屬性來控制某幾個字段不出現在toString方法的結果中;
  2. 使用includeFieldNames屬性來控制是否在toString方法的結果中出現成員變量的名稱;
  3. 使用doNotUseGetters屬性來控制在toString方法中是否使用getter方法來訪問變量的值;
  4. 使用callSuper屬性來控制是否要調用父類的toString方法,即在子類的toString方法輸出時,是否同時將父類的成員一同輸出;

@EqualsAndHashCode

        在項目開發過程中,雖然場景比較少,但仍然不可避免存在需要自定義equals或者hashCode方法的時候,當然這也是頭疼的時候。根據《Effective Java》中的建議,equals方法和hashCode方法要同時實現,並且保證一致性。Lombok提供的@EqualsAndHashCode完美的解決了手寫equals和hashCode方法時遇到的全部問題,不需要刻意關注底層的實現細節。如下是使用@EqualsAndHashCode的一個樣例。

@EqualsAndHashCode的樣例
樣例 生成的代碼
@EqualsAndHashCode(exclude = { "age", "job" }, doNotUseGetters = true)
class Staff
{
    private String name;
    private int age;
    private String job;
    private Object info;
}

@EqualsAndHashCode(callSuper = false)
class Student extends Staff
{
    private String schoolName;
    private int classNo;
}
class Staff
{
    private String name;
    private int age;
    private String job;
    private Object info;

    @Override
    public int hashCode()
    {
        final int PRIME = 31;
        int result = 1;
        final Object $name = name;
        result = result * 31 + ($name == null ? 0 : $name.hashCode());
        final Object $info = info;
        result = result * 31 + ($info == null ? 0 : $info.hashCode());
        return result;
    }

    public boolean canEqual(final Object other)
    {
        return other instanceof Staff;
    }

    @Override
    public boolean equals(final Object o)
    {
        if (o == this)
        {
            return true;
        }
        if (!(o instanceof Staff))
        {
            return false;
        }
        final Staff other = (Staff) o;
        if (!other.canEqual(this))
        {
            return false;
        }
        final Object this$name = name;
        final Object other$name = other.name;
        if (this$name == null ? other$name != null : !this$name.equals(other$name))
        {
            return false;
        }
        final Object this$info = info;
        final Object other$info = other.info;
        return this$info == null ? other$info == null : this$info.equals(other$info);
    }
}

class Student extends Staff
{
    private String schoolName;
    private int classNo;

    @Override
    public int hashCode()
    {
        final int PRIME = 31;
        int result = 1;
        final Object $schoolName = schoolName;
        result = result * 31 + ($schoolName == null ? 0 : $schoolName.hashCode());
        result = result * 31 + classNo;
        return result;
    }

    @Override
    public boolean canEqual(final Object other)
    {
        return other instanceof Student;
    }

    @Override
    public boolean equals(final Object o)
    {
        if (o == this)
        {
            return true;
        }
        if (!(o instanceof Student))
        {
            return false;
        }
        final Student other = (Student) o;
        if (!other.canEqual(this))
        {
            return false;
        }
        final Object this$schoolName = schoolName;
        final Object other$schoolName = other.schoolName;
        if (this$schoolName == null ? other$schoolName != null : !this$schoolName.equals(other$schoolName))
        {
            return false;
        }
        return classNo == other.classNo;
    }
}

        只要在class關鍵字前增加@EqualsAndHashCode,Lombok就可以爲我們生成一大票代碼,相當給力。

        樣例使用了@EqualsAndHashCode的如下能力:

  1. 使用exclude屬性來控制某幾個字段不出現在equals和hashCode方法中;
  2. 使用doNotUseGetters屬性來控制在equals和hashCode方法中是否使用getter方法來訪問變量的值;
  3. 使用callSuper屬性來控制是否要調用父類的equals和hashCode方法;

@Getter和@Setter

        通過這兩個註解,可以靈活控制是否爲字段提供getter/settere方法,以及getter/setter方法的訪問權限。由於使用非常簡單,直接見樣例。

@Getter和@Setter的樣例
樣例 生成的代碼
class Student
{
    @Getter
    @Setter
    private String name;

    @Getter
    private String schoolName;

    @Setter
    private int classNo;

    @Getter(AccessLevel.MODULE)
    private int age;

    @Getter(AccessLevel.NONE)
    private String job;

    @Getter(AccessLevel.PRIVATE)
    private Object info;
}
class Student
{
    private String name;
    private String schoolName;
    private int classNo;
    private int age;
    private String job;
    private Object info;

    public String getName()
    {
        return name;
    }

    public void setName(final String name)
    {
        this.name = name;
    }

    public String getSchoolName()
    {
        return schoolName;
    }

    public void setClassNo(final int classNo)
    {
        this.classNo = classNo;
    }

    int getAge()
    {
        return age;
    }

    private Object getInfo()
    {
        return info;
    }
}

        通常情況下,在每個成員變量上手寫@Getter或者@Setter比較麻煩,但優點是可以靈活控制getter或者setter方法的訪問權限,具體可以參考lombok.AccessLevel類的定義,由於非常好理解,這裏不再贅述。

@Data

        @Data的作用相當於是@ToString、@EqualsAndHashCode、@Getter、@Setter效果的集合,因而不再給出樣例。

其它

        Lombok還提供了其它很多有用的註解,但由於在本人的項目中沒有應用到,所以沒有使用經驗,有興趣的朋友可以直接參考官網的資料。


發佈了70 篇原創文章 · 獲贊 52 · 訪問量 28萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章