This is the fourth day of my participation in the August More text Challenge. For details, see: August More Text Challenge

1 introduction

I recently finished reading refactoring

In the chapter on removing Settings, I discovered that I had been working with Lombok for so long that I was used to using the @data annotation for Getter/Setter configuration

So how do you remove unnecessary setters in Lombok?

Starting with this question, you can extend it to “How do YOU constrain and refactor Java classes under Lombok?”

Due to the limited ability of the author, it can only be summarized from the following aspects:

  1. Modifiers in Java
  2. Refactoring strategies in Lombok
  3. Possible problems with Lombok

Modifiers in Java

There are four kinds of modifiers in Java: public/default/protected/private

The special one is default, which has the same meaning as not writing any modifiers

The modifier’s accessibility range is as follows:

The modifier Inside the class A subclass The same package other
public Square root Square root Square root Square root
default Square root ? Square root x
protected Square root Square root x x
private Square root x x x

It is not clear whether the subclasses of default can be accessed, because the access condition of default is under the same package. If the subclasses are not under the same package, they cannot be accessed

Why do we introduce modifiers?

For a good program, access should be clear for every property of every class

Even with Lombok, we don’t apply the @data solve everything approach to every class that passes through our hands

Both fields/methods and getters/setters should have their own access control

If you can agree with that, the following will certainly help you

Refactoring strategies in Lombok

3.1 Self-encapsulated Fields

To Lombok’s credit, it frees up the programmer’s hands and visual sense with this refactoring strategy

  1. Change the properties of the class toprivate
  2. Increments in the class header@Dataannotations

For example, we create the Account class:

@Data
public class Account {

    private String id;

}
Copy the code

You can only call the get/set method when setting the value:

public class Test {

    public static void main(String[] args) {
        Account account = new Account();
        // The id attribute cannot be accessed directly
        account.id = "test-01";
        account.setId("test-01"); account.getId(); }}Copy the code

3.2 Removing the Settings function

When a field is not allowed to change after creation, we should cancel its setting function

Continuing with the kulombok, under this refactoring strategy, we only need to make the field variables final:

@Data
public class Account {

    private final String id;

}
Copy the code
public class Test {

    public static void main(String[] args) {
        Account account = new Account("test-01");
        // The setId method does not exist
        account.setId("test-01"); }}Copy the code

3.3 Factory pattern replaces constructor

We should use factory functions when creating objects that do more than simply build

The Builder in Lombok provides a degree of traversal for factory functions

Suppose we create a Person class with a gender attribute where a value of 0 represents women and a value of 1 represents men:

@Data
public class Person {

    public Person(int gender) {
        this.gender = gender;
    }

    private int gender;

}
Copy the code

Now we create two special factory functions: create male and create female:

@Data
public class Person {

    public Person(int gender) {
        this.gender = gender;
    }

    private int gender;

    public static Person createFemale(a) {
        return new Person(0);
    }

    public static Person createMale(a) {
        return new Person(1); }}Copy the code

Let’s use Builder to modify the Person class:

@Data
@Builder
public class Person {

    private int gender;

    public static Person createFemale(a) {
        return Person.builder().gender(0).build();
    }

    public static Person createMale(a) {
        return Person.builder().gender(1).build(); }}Copy the code

At first glance, it doesn’t seem to make much difference, mainly because our Person class is very simple. What if:

  1. The Person class has a total of 20 arguments, 12 of which need to be used in the constructor
  2. The Person class is a subclass of a class. The constructor needs to call the parent class, and the parent class’s constructor needs eight more parameters to pass
  3. In both cases, suddenly adjusting the Construction rules of the Person class at the end of development,Then you might go crazy, and if there’s inheritance, you’ll go crazy again when you change the parent class

In factory functions, it is recommended to use @Builder and @superBuilder properly

4 possible problems with Lombok

4.1 Encapsulating A Collection

When there is a set of properties in a class, it is important to be extra vigilant. It is best to follow the following principles:

  1. You cannot directly manipulate a collection outside of a class
  2. You cannot directly assign a value to the entire property outside of the class
  3. The operations needed to implement the collection in the class

Taking arrays as an example, the corresponding implementation of the above three principles is:

  1. The getter should return an immutable copy of the array
  2. Remove the setter, add the initialization function, and its contents should be either traversal added or deep copy
  3. Add the add and remove methods

When using Lombok’s @data, the above principles are not possible and we need to adjust them

Continue with the Person class, assuming that everyone has courses to learn, and modify it with Lombok according to the encapsulating collection principle:

@Data
public class Person {

    @Setter(AccessLevel.NONE)
    private List<String> courses = new ArrayList<>();

    public List<String> getCourses(a) {
        return Collections.unmodifiableList(this.courses);
    }

    public void initCourses(List<String> courses) {
        assert this.courses.isEmpty() : "The course array is not empty and cannot be initialized.";
        this.courses.addAll(courses);
    }

    public void addCourse(String course) {
        this.courses.add(course);
    }

    public void removeCourse(String course) {
        this.courses.remove(course); }}Copy the code

Note: You added the assert keyword to the initCourses method. This feature is disabled by default when running Java programs and needs to be manually enabled by adding VM parameters

The actual effect is as follows:

public class Test {

    public static void main(String[] args) {
        // after initialization, the courses are []
        Person person = new Person();
        // The setCourses method does not exist
        person.setCourses(Arrays.asList("test-01"."test-02"));
        // ["test-01", "test-02"]
        person.initCourses(Arrays.asList("test-01"."test-02"));
        // The assert keyword failed to be initialized twice
        person.initCourses(Arrays.asList("test-01"."test-02"));
        // ["test-01", "test-02", "test-03"]
        person.addCourse("test-03");
        // ["test-01", "test-02"]
        person.removeCourse("test-03");
        // Error: the getter method has a read-only copy and cannot call add/remove, etc
        person.getCourses().add("test-04"); }}Copy the code

5 concludes

I have always believed that laziness is a necessary condition for progress

But laziness here means that in terms of the amount of code/keystrokes/mental burden, learning and thinking can’t be lazy

Lombok helps us simplify our code, which is a good thing

But we should learn to be flexible about collections and the special needs of the business

There is no best technology in the world, only the most elegant way to use it